Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hackで作る堅実な アプリケーションアーキテクチャ / Hack-application-architecture

Hackで作る堅実な アプリケーションアーキテクチャ / Hack-application-architecture

PHP Conference Sendai 2019で利用したスライドです

yuuki takezawa

January 22, 2019
Tweet

More Decks by yuuki takezawa

Other Decks in Programming

Transcript

  1. HackͰ࡞Δݎ࣮ͳ
    ΞϓϦέʔγϣϯΞʔΩςΫνϟ
    yuuki takezawa
    PHP Conference Sendai 2019

    View Slide

  2. Profile
    • ஛ᖒ ༗و / ytake
    • גࣜձࣾΞΠελΠϧ CTO
    • PHP, Hack, Go, Scala
    • Apache Hadoop, Apache Spark, Apache Kafka

    • twitter https://twitter.com/ex_takezawa
    • facebook https://www.facebook.com/yuuki.takezawa
    • github https://github.com/ytake

    View Slide

  3. View Slide

  4. View Slide

  5. HackΛ࢖ͬͨΒ۩ମతʹԿ͕Ͱ͖Δͷʁ
    ۩ମతͳ಺༰Λ஌Γ͍ͨʂ

    View Slide

  6. Agenda
    • PHP Array / Hack Arrays
    • Shapes In Hack
    • Generics (With Dependency Injection)
    • Trait And Interface Requirements
    • Implementing DDD / Generic Repository

    View Slide

  7. ࠓճ͸HHVM 3.27Ҏ߱ʹରԠ͢Δ࿩Ͱ͢
    ݹ͍όʔδϣϯΛར༻͍ͯ͠Δ৔߹͸
    ΞοϓσʔτΛ

    View Slide

  8. HHVM 3.30(Latest)

    View Slide

  9. Notice
    • PHPޓ׵͕αϙʔτ͞ΕΔ࠷ޙͷόʔδϣϯ
    • hhvm.enable_php=false 

    PHPίʔυΛແޮԽ
    • HackTest΍CLIɺIOपΓͷϥΠϒϥϦ͕ॆ࣮
    • HHVMͰPHPΛಈ࡞ͤ͞ΔΑΓ΋Hackͱͯ͠

    View Slide

  10. Notice
    • GitHubͷfacebook/hhvmʹهࡌ͞Ε͍ͯΔ

    Prebuilt Packages on Centos 7.x͸ݹ͍

    • Ubuntuɺ·ͨ͸macOSਪ঑

    View Slide

  11. Hack͔PHP͔
    • HHVM্ͰPHPΛಈ͔͢ϝϦοτ͸ແ͠

    Ҏલ͸ߴ଎ʹಈ͘؀ڥɺͱͯ͠ೝ஌͞Ε͍ͯͨ

    PHPࣗମ͕ߴ଎Խ͞ΕɺHHVMࣗମ΋Hackઐ༻ʹ
    • ଎౓Ͱ͸ͳ͘ɺ

    ੩తܕ෇ݴޠϥΠΫͳػೳ͕ཉ͍͔͠Ͳ͏͔

    ࣮༻తͳΞϓϦέʔγϣϯαʔό͕ཉ͍͔͠Ͳ͏͔

    async΍ɺxhpͳͲͷχʔζ͕͋Δ͔Ͳ͏͔

    View Slide

  12. Hackपลπʔϧͷ࿩
    • composer͸ɺhhvm/hh-autoloadͱҰॹʹ࢖͏͜ͱ
    • Hackͷ։ൃ؀ڥ͸ɺIDEαϙʔτ͕ڧྗ

    vscodeɺnuclide
    • hhast͕linterͱͯ͠ػೳ

    ίʔυͷมߋͳͲ΋΍ͬͯ͘ΕΔ
    • HHVM/Hackࣗମ͕։ൃαϙʔτػೳଟ͠

    View Slide

  13. Ͱ΋PHPͷϥΠϒϥϦ
    ࢖͑ͳ͍ΜͰ͠ΐ͏ʁ

    View Slide

  14. Hack ϥΠϒϥϦ
    • facebook/hack-router Hackઐ༻ϧʔλʔ
    • facebook/hh-clilib CLIϥΠϒϥϦ
    • usox/hackttp Hack HTTPΠϯλʔϑΣʔε࣮૷
    • ytake/hungrr Hack HTTPΠϯλʔϑΣʔε࣮૷
    • hhpack/service-locator αʔϏεϩέʔλʔɾDI
    • nazg/glue αʔϏεϩέʔλʔɾDI
    • nazg/heredity ϛυϧ΢ΣΞσΟεύονϟ
    • nazg/hcache Ωϟογϡ

    View Slide

  15. PHP Array / Hack Arrays

    View Slide

  16. PHPͱHack
    • PHPͰ͸഑ྻ͸arrayͷΈͰදݱ͞ΕΔ

    ഑ྻࣗମʹܕΛࢦఆ͢Δ͜ͱ͸Ͱ͖ͳ͍
    • HackͰ഑ྻ͸arrayɺdarrayɺvarrayͷ3छ

    ͲΜͳ഑ྻͰ΋ܕΛࢦఆ͠ͳ͚Ε͹ͳΒͳ͍

    View Slide

  17. declare(strict_types=1);
    class ArraySet
    {
    protected $collect = [];
    public function add(string $key, string $value): void
    {
    $this->collect[$key] = $value;
    }
    public function all(): array
    {
    return $this->collect;
    }
    }

    View Slide

  18. declare(strict_types=1);
    class ArraySet
    {
    protected $collect = [];
    public function add(string $key, string $value): void
    {
    $this->collect[$key] = $value;
    }
    public function all(): array
    {
    return $this->collect;
    }
    }
    ෆ࣮֬ͳܕͷࠞೖΛ๷͙ͨΊ

    ೖྗΛ੍ݶ

    View Slide

  19. declare(strict_types=1);
    class ArraySet
    {
    protected $collect = [];
    public function add(string $key, string $value): void
    {
    $this->collect[$key] = $value;
    }
    public function all(): array
    {
    return $this->collect;
    }
    }
    ੍ޚෆՄ

    View Slide

  20. class ExtendArraySet extends ArraySet
    {
    public function all(): array
    {
    $this->collect[1] = 2;
    return $this->collect;
    }
    }
    ੍ޚෆՄ

    View Slide

  21. View Slide

  22. class ArraySet {
    protected darray $collect = [];
    public function add(string $key, string $value): void {
    $this->collect[$key] = $value;
    }
    public function all(): darray {
    return $this->collect;
    }
    }

    View Slide

  23. class ArraySet {
    protected darray $collect = [];
    public function add(string $key, string $value): void {
    $this->collect[$key] = $value;
    }
    public function all(): darray {
    return $this->collect;
    }
    }
    ෆ࣮֬ͳܕͷࠞೖΛ๷͙ͨΊ

    ೖྗΛ੍ݶ

    View Slide

  24. class ArraySet {
    protected darray $collect = [];
    public function add(string $key, string $value): void {
    $this->collect[$key] = $value;
    }
    public function all(): darray {
    return $this->collect;
    }
    }
    ܧঝ࣌Ͱ΋ͦͷ··੍ޚՄ

    View Slide

  25. class ExtendArraySet extends ArraySet
    {
    public function all(): darray
    {
    $this->collect[1] = 2;
    return $this->collect;
    }
    } ૠೖෆՄ

    View Slide

  26. class ExtendArraySet extends ArraySet
    {
    public function all(): darray
    {
    $this->collect[1] = 2;
    return $this->collect;
    }
    }
    ໭ΓͷܕΛมߋ͢Δ͜ͱ΋ෆՄ

    View Slide

  27. src/ArraySet.php:19:5,25: Invalid assignment (Typing[4110])
    src/ArraySet.php:5:20,25: This is a string
    src/ArraySet.php:19:20,20: It is incompatible with an int
    src/ArraySet.php:20:12,25: Invalid return type (Typing[4110])
    src/ArraySet.php:18:33,38: This is a string
    src/ArraySet.php:19:20,20: It is incompatible with an int

    View Slide

  28. public function merge(): darray {
    $a = new ArraySet();
    $a->add('PHP', 'Arrays');
    $ar = $a->all();
    $ar[1] = 11;
    return $ar;
    }
    औಘޙɺ
    ҟͳΔܕͷ஋Λࠞೖͤ͞Δ͜ͱ͸Մೳ
    *໭ΓͷܕΛఆٛ͢Δ͜ͱ

    View Slide

  29. PHP Style Arrays
    • array

    ௨ৗͷ഑ྻ
    • varray

    ஋ͷΈͰߏ੒͞ΕΔ഑ྻ
    • darray

    σΟΫγϣφϦͷ഑ྻ

    View Slide

  30. class Sample {
    protected varray $varray = varray[
    'php',
    'hack'
    ];
    protected darray $darray = darray[
    'testing' => 'testing',
    1 => 'testing'
    ];
    public function failedVArray(): varray {
    return $this->varray;
    }
    public function getDArray(): darray {
    return $this->darray;
    }
    }

    View Slide

  31. class Sample {
    protected varray $varray = varray[
    'php',
    'hack'
    ];
    protected darray $darray = darray[
    'testing' => 'testing',
    1 => 'testing'
    ];
    public function failedVArray(): varray {
    return $this->varray;
    }
    public function getDArray(): darray {
    return $this->darray;
    }
    }
    ܕҧ͍

    View Slide

  32. class Sample {
    protected varray $varray = varray[
    'php',
    'hack'
    ];
    protected darray $darray = darray[
    'testing' => 'testing',
    1 => 'testing'
    ];
    public function failedVArray(): varray {
    return $this->varray;
    }
    public function getDArray(): darray {
    return $this->darray;
    }
    }
    ໭Γͷܕҧ͍

    View Slide

  33. class Sample {
    protected varray $varray = varray[
    'php',
    'hack'
    ];
    protected darray $darray = darray[
    'testing' => 'testing',
    1 => 'testing'
    ];
    public function failedVArray(): varray {
    return $this->varray;
    }
    public function getDArray(): darray {
    return $this->darray;
    }
    }
    ໭Γͷܕҧ͍

    View Slide

  34. Hack Arrays
    • vec

    ७ਮ഑ྻΛѻ͏഑ྻ

    VectorɺImmVectorஔ͖׵͑Մ
    • dict

    keyͱvalueͰߏ੒͞ΕΔ഑ྻ

    MapɺImmMapஔ͖׵͑Մ

    View Slide

  35. Hack Arrays
    • keyset

    ॏෳ͠ͳ͍஋Λ࣋ͭ७ਮ഑ྻ

    SetɺImmSetஔ͖׵͑Մ


    View Slide

  36. class ArraySet {
    protected dict $collect = dict[];
    public function add(string $key, string $value): void {
    $this->collect[$key] = $value;
    }
    public function all(): dict {
    return $this->collect;
    }
    }
    dictࢦఆ

    View Slide

  37. Hack Arrays
    • Hack Arrays͸

    isԋࢉࢠ(HackͷΈͷԋࢉࢠ)Λ༻͍ͯ൑ఆՄೳ

    $dict is dict<_, _> , etc
    • Hack Arrays͸ͦΕͧΕͷ഑ྻʹม׵Մೳ

    (Collection͔ΒHack Arrays΍ͦͷٯ΋)

    View Slide

  38. $vec = vec[1,1,2,3,4,5,6,6,7,8];
    var_dump($vec);
    var_dump(dict($vec));
    var_dump(keyset($vec));
    $dict = dict[1 => 2, 3 => 'testing'];
    var_dump(vec($dict), $dict is dict<_, _>);

    View Slide

  39. abstract final class dict<+Tk as arraykey, +Tv>
    implements Indexish, XHPChild {}
    abstract final class keyset<+T as arraykey>
    implements Indexish, XHPChild {}
    abstract final class vec<+T>
    implements Indexish, XHPChild {}

    View Slide

  40. Hack Collections
    • PHPϥΠΫͳ഑ྻ΍ɺHack ArraysΑΓ΋

    ݎ͍ίϨΫγϣϯ
    • ͲΕΛར༻͢Δ͔͸ઃܭɾ࣮૷࣍ୈ

    foreachΑΓ΋

    filter΍mapͱ͍ͬͨॲཧΛ͢ΔͷͰ͋Ε͹Collections

    • cloneޙʹ஋͕มߋ͞ΕΔ(ϝϞϦ)

    View Slide

  41. class CollectionSet {
    protected Map $collect = Map{};
    public function add(string $key, string $value): void {
    $this->collect[$key] = $value;
    }
    public function all(): Map {
    return $this->collect;
    }
    }
    Mapࢦఆ

    View Slide

  42. class CollectionSet {
    protected Map $collect = Map{};
    public function add(string $key, string $value): void {
    $this->collect[$key] = $value;
    }
    public function all(): Map {
    return $this->collect;
    }
    }
    ഑ྻͱಉ͡Α͏ʹૠೖ

    View Slide

  43. ૠೖෆՄ
    $c = (new CollectionSet())->all();
    $c->add(Pair{1, 2});

    View Slide

  44. Notice: Hack Arrays / Collections
    • HackͰ͸issetɺunset͸ར༻Ͱ͖ͳ͍
    • unset͸removeɺfilter(Collection)΍

    hhvm/hslΛར༻͢Δ͜ͱ
    • isset͸contains(Collection)΍array_key_existsɺ

    hhvm/hslར༻

    View Slide

  45. class HackMap {
    protected Map $collect = Map{
    'a' => 'b',
    'c' => 'd',
    };
    public function filterRemove(
    string $keyName
    ): Map {
    return $this->collect
    ->filterWithKey(($key, $_) ==> $key !== $keyName);
    }
    public function remove(
    string $keyName
    ): Map {
    return $this->collect->remove($keyName);
    }
    }
    ࢦఆͨ͠ΩʔҎ֎ͷ΋ͷΛ
    MapͰฦ٫

    View Slide

  46. class HackMap {
    protected Map $collect = Map{
    'a' => 'b',
    'c' => 'd',
    };
    public function filterRemove(
    string $keyName
    ): Map {
    return $this->collect
    ->filterWithKey(($key, $_) ==> $key !== $keyName);
    }
    public function remove(
    string $keyName
    ): Map {
    return $this->collect->remove($keyName);
    }
    }
    ࢦఆͨ͠ΩʔΛ࡟আ͠ɺ
    MapͰฦ٫

    View Slide

  47. Hack Arrays / Collections
    • array͚ͩͰ͸੍ݶͰ͖ͳ͍΋ͷΛ੍ݶ
    • PHP͔ΒҠߦ͢Δ৔߹͸·͔ͣ͜͜Β

    Ұ൪ޮՌ͕େ͖͍
    • arrayૢ࡞ͱ΄΅ಉ͡ͰɺͦΕΒͷPHPؔ਺͕ར༻Մ

    View Slide

  48. Shapes In Hack

    View Slide

  49. Shapes?
    • ഑ྻͷϑΟʔϧυʹରͯ͠ܕΛఆٛ͢Δ΋ͷ

    • TypecheckerͰϑΟʔϧυΛ൑ఆͰ͖ΔͨΊɺ

    ෆཁͳϑΟʔϧυͷ௥Ճ΍ɺෆ࣮֬ͳ஋ΛഉআͰ͖Δ
    • ഑ྻͦͷ΋ͷͰ͸ͳ͍ͨΊɺforeach΍ [] ΦϖϨʔλ͸

    ར༻ෆՄ

    View Slide

  50. declare(strict_types=1);
    class GenerateArrayField
    {
    public function getArray(): array
    {
    return [
    'q' => 'qwerty',
    'w' => 'wertyu',
    ];
    }
    }

    View Slide

  51. declare(strict_types=1);
    class GenerateArrayField
    {
    public function getArray(): array
    {
    return [
    'q' => 'qwerty',
    'w' => 'wertyu',
    ];
    }
    } ഑ྻͷϑΟʔϧυʹରͯ͠

    ੍ݶෆՄ

    View Slide

  52. View Slide

  53. type SampleShape = shape('q' => string, 'w' => string);
    class GenerateArrayField {
    public function getArray(): SampleShape {
    return shape(
    'q' => 'qwerty',
    'w' => 'wertyu',
    );
    }
    }

    View Slide

  54. type SampleShape = shape('q' => string, 'w' => string);
    class GenerateArrayField {
    public function getArray(): SampleShape {
    return shape(
    'q' => 'qwerty',
    'w' => 'wertyu',
    );
    }
    }
    ഑ྻͷϑΟʔϧυʹܕఆٛ

    View Slide

  55. type SampleShape = shape('q' => string, 'w' => string);
    class GenerateArrayField {
    public function getArray(): SampleShape {
    return shape(
    'q' => 'qwerty',
    'w' => 'wertyu',
    );
    }
    }
    shapeΛ࢖ͬͯهड़

    View Slide

  56. public function getArrays(): vec {
    return vec[
    shape('q' => 'qwerty', 'w' => 'wertyu',),
    shape('q' => 'PHP', 'w' => 'Hack',),
    shape('q' => 'Java', 'w' => 'Scala',),
    ];
    }
    vec഑ྻʹshapesΛ

    ૊Έ߹ΘͤͯΑΓ࣮֬ͳ഑ྻ΁

    View Slide

  57. namespace Acme\Shapes;
    type SampleShape = shape(
    'a' => int,
    'b' => int
    );
    type NestShape = shape(
    'sample' => SampleShape,
    'vec' => vec
    );
    class NestedShape {
    public function nest(): NestShape {
    return shape(
    'sample' => shape(
    'a' => 0,
    'b' => 1
    ),
    'vec' => vec[
    'shapes'
    ],
    );
    }
    }
    Shapesಉ࢜Λ૊Έ߹Θͤͯ

    ఆٛՄ

    View Slide

  58. The field 'z' is not defined in this shape type,
    and this shape type does not allow unknown fields.
    The field 'z' is set in the shape.
    ఆٛ͞Ε͍ͯͳ͍ϑΟʔϧυ
    ར༻࣌ʹΤϥʔ

    View Slide

  59. Shapes?
    • ଞݴޠͷStructͷΑ͏ʹϑΟʔϧυͷܕએݴ
    • APIͳͲͷόϦσʔγϣϯ΍ɺ

    ϝοηʔδϒϩʔΧʔ΁ͷ௨৴ͳͲʹ
    • PHPؔ਺ͷ໭Γ஋ͱ૊Έ߹Θͤͯݫ֨ʹ
    • PHPʹಉ౳ػೳ͸ແ͠

    View Slide

  60. class HalShape {
    const type embeddedLinks = shape(
    'name' => string,
    '_links' => shape(
    'self' => shape(
    'href' => string
    )
    )
    );
    public function index(): void {
    $url = [
    'name' => 'hack',
    '_links' => [
    'self' => [
    'href' =>'uri'
    ]
    ]
    ];
    \var_dump($url as this::embeddedLinks);
    }
    }

    View Slide

  61. class HalShape {
    const type embeddedLinks = shape(
    'name' => string,
    '_links' => shape(
    'self' => shape(
    'href' => string
    )
    )
    );
    public function index(): void {
    $url = [
    'name' => 'hack',
    '_links' => [
    'self' => [
    'href' =>'uri'
    ]
    ]
    ];
    \var_dump($url as this::embeddedLinks);
    }
    }
    type constantsΛ࢖ͬͯ

    shapeΛهड़

    View Slide

  62. class HalShape {
    const type embeddedLinks = shape(
    'name' => string,
    '_links' => shape(
    'self' => shape(
    'href' => string
    )
    )
    );
    public function index(): void {
    $url = [
    'name' => 'hack',
    '_links' => [
    'self' => [
    'href' =>'uri'
    ]
    ]
    ];
    \var_dump($url as this::embeddedLinks);
    }
    }
    ഑ྻͳͲͰ஋Λهड़

    View Slide

  63. class HalShape {
    const type embeddedLinks = shape(
    'name' => string,
    '_links' => shape(
    'self' => shape(
    'href' => string
    )
    )
    );
    public function index(): void {
    $url = [
    'name' => 'hack',
    '_links' => [
    'self' => [
    'href' =>'uri'
    ]
    ]
    ];
    \var_dump($url as this::embeddedLinks);
    }
    }
    shapeͷఆٛΛݩʹ഑ྻΛݕࠪ

    View Slide

  64. Shapes?
    • ϑΟʔϧυࣗମ͕nullableͷ৔߹͸ɺϑΟʔϧυ໊ʹ?
    • ϑΟʔϧυͷ஋͕nullableͷ৔߹͸ɺܕʹ?
    • ShapeͷϑΟʔϧυΞΫηε࣌ͷσϑΥϧτ஋͸

    Shapes::idx($shape, $fieldName, 'default')
    • ഑ྻͳͲͷม׵͸toArray΍toDictͳͲ

    View Slide

  65. Hack Arrays + Shapes
    • ͜ΕΒ͸શͯTypechekcerʹΑΔܕνΣοΫͰ൑ఆՄ
    • ܕʹؔ͢Δίʔυ͕େ͖͘ݮΔ
    • ܕʹؔ͢ΔίʔυϨϏϡʔ࣌ؒ࡟ݮ
    • جຊతʹ͸strictͰهड़͢Δ͜ͱ

    View Slide

  66. Hack Application Architecture

    View Slide

  67. Dependency Injection

    View Slide

  68. DI Container
    • PHP༻ͷϥΠϒϥϦΛར༻Ͱ͖Δ͕ɺ

    4ܥ͔Βར༻Ͱ͖ͳ͍ͨΊɺϝϦοτແ͠
    • PSR-11Λͦͷ··ར༻͢Δ

    ΋͘͠͸ܕʹΑΔ੍໿Λݫͨ͘͠͠΋ͷΛ։ൃ

    View Slide

  69. DI Container
    • ίϯςφ͔ΒΠϯελϯεΛ࣮֬ʹऔಘ͢ΔͨΊʹ

    ొ࿥͸Ϋϥε໊ͷΈ

    • mixedΛϦλʔϯ͢Δ৔߹͸ɺ

    ར༻࣌ʹܕνΣοΫΛߦΘͳ͚Ε͹ͳΒͳ͍(Typecheker)
    • औಘ࣌͸ΦϒδΣΫτͷΈฦ٫ͱ͢Δ


    View Slide

  70. Ϋϥε໊Λදݱ
    • PHPͰ͸Ϋϥε໊͸stringͰ͔͠ͳ͍ͨΊɺ

    ଘࡏ͢ΔΫϥε͔Ͳ͏͔ɺݫ֨ͳࢦఆ͸Ͱ͖ͳ͍
    • HackͰ͸ stringͰ͸ͳ͘ classname Λར༻

    ::class ར༻͢Δͱ͜ͷܕʹ
    • GenericsΛซ༻

    View Slide

  71. $container = new Container();
    $container->set(
    \stdClass::class,
    ($container) ==> new \stdClass()
    );
    $stdClass = $container->getInstance(\stdClass::class);
    ೖྗΛΫϥε໊ͷΈͱ੍ͯ͠ݶ

    View Slide

  72. $container = new Container();
    $container->set(
    \stdClass::class,
    ($container) ==> new \stdClass()
    );
    $stdClass = $container->getInstance(\stdClass::class);
    Typechecker͕൑ఆͰ͖ͳ͍৔߹͸ɺ
    isͳͲΛར༻ͯ͠อূ͢Δඞཁ͕͋Δ

    View Slide

  73. namespace Acme;
    use namespace HH\Lib\{C, Str};
    class Container {
    private dict<
    string,
    (Scope, (function(\Acme\Container): T))
    > $map = dict[];
    public function set(
    classname $id,
    (function(\Acme\Container): T) $callback,
    Scope $scope = Scope::PROTOTYPE,
    ): void {
    $this->map[$id] = tuple($scope, $callback);
    }

    View Slide

  74. namespace Acme;
    use namespace HH\Lib\{C, Str};
    class Container {
    private dict<
    string,
    (Scope, (function(\Acme\Container): T))
    > $map = dict[];
    public function set(
    classname $id,
    (function(\Acme\Container): T) $callback,
    Scope $scope = Scope::PROTOTYPE,
    ): void {
    $this->map[$id] = tuple($scope, $callback);
    }
    Genericsར༻

    View Slide

  75. namespace Acme;
    use namespace HH\Lib\{C, Str};
    class Container {
    private dict<
    string,
    (Scope, (function(\Acme\Container): T))
    > $map = dict[];
    public function set(
    classname $id,
    (function(\Acme\Container): T) $callback,
    Scope $scope = Scope::PROTOTYPE,
    ): void {
    $this->map[$id] = tuple($scope, $callback);
    }
    dictΛར༻͠ɺ
    ίʔϧόοΫͰTΛฦ٫͢Δͱఆٛ

    View Slide

  76. namespace Acme;
    use namespace HH\Lib\{C, Str};
    class Container {
    private dict<
    string,
    (Scope, (function(\Acme\Container): T))
    > $map = dict[];
    public function set(
    classname $id,
    (function(\Acme\Container): T) $callback,
    Scope $scope = Scope::PROTOTYPE,
    ): void {
    $this->map[$id] = tuple($scope, $callback);
    }
    classname 

    ͭ·Γଘࡏ͢ΔΫϥε໊ͷΈڐՄͱ͢Δ

    View Slide

  77. namespace Acme;
    use namespace HH\Lib\{C, Str};
    class Container {
    private dict<
    string,
    (Scope, (function(\Acme\Container): T))
    > $map = dict[];
    public function set(
    classname $id,
    (function(\Acme\Container): T) $callback,
    Scope $scope = Scope::PROTOTYPE,
    ): void {
    $this->map[$id] = tuple($scope, $callback);
    }
    tupleͰΠϯελϯεੜ੒ํ๏ͱɺ
    ίʔϧόοΫΛอ࣋

    View Slide

  78. dictૢ࡞
    • dictʹొ࿥ࡁΈ͔Ͳ͏͔͸array_key_existsɺ

    ·ͨ͸ϥούʔؔ਺Λར༻
    • Πϯελϯεੜ੒ํ๏ʹΑͬͯϝϞԽ͢Δ͔Ͳ͏͔

    View Slide

  79. <<__Rx>>
    public function getInstance(classname $t): T {
    return $this->resolve($t);
    }
    <<__Memoize>>
    protected function shared(classname $id): T {
    list($_, $callable) = $this->map[$id];
    return $callable($this);
    }
    <<__Rx>>
    public function has(string $id): bool {
    return C\contains_key($this->map, $id);
    }

    View Slide

  80. <<__Rx>>
    public function getInstance(classname $t): T {
    return $this->resolve($t);
    }
    <<__Memoize>>
    protected function shared(classname $id): T {
    list($_, $callable) = $this->map[$id];
    return $callable($this);
    }
    <<__Rx>>
    public function has(string $id): bool {
    return C\contains_key($this->map, $id);
    }
    hhvm/hsl

    array_key_existsϥούʔؔ਺

    View Slide

  81. <<__Rx>>
    public function getInstance(classname $t): T {
    return $this->resolve($t);
    }
    <<__Memoize>>
    protected function shared(classname $id): T {
    list($_, $callable) = $this->map[$id];
    return $callable($this);
    }
    <<__Rx>>
    public function has(string $id): bool {
    return C\contains_key($this->map, $id);
    }
    ϝϞԽࢦఆ
    Ҿ਺Λར༻ͯ͠Ωϟογϡ͞ΕΔ
    γϯάϧτϯ

    View Slide

  82. <<__Rx>>
    public function getInstance(classname $t): T {
    return $this->resolve($t);
    }
    <<__Memoize>>
    protected function shared(classname $id): T {
    list($_, $callable) = $this->map[$id];
    return $callable($this);
    }
    <<__Rx>>
    public function has(string $id): bool {
    return C\contains_key($this->map, $id);
    }
    reactiveʹ

    View Slide

  83. <<__Rx>>
    protected function resolve(classname $id): T {
    if ($this->has($id)) {
    list($scope, $callable) = $this->map[$id];
    if ($callable is nonnull) {
    if ($scope === Scope::SINGLETON) {
    return $this->shared($id);
    }
    return $callable($this);
    }
    }
    throw new Exception\NotFoundException(
    Str\format('Identifier "%s" is not binding.', $id),
    );
    }

    View Slide

  84. <<__Rx>>
    protected function resolve(classname $id): T {
    if ($this->has($id)) {
    list($scope, $callable) = $this->map[$id];
    if ($callable is nonnull) {
    if ($scope === Scope::SINGLETON) {
    return $this->shared($id);
    }
    return $callable($this);
    }
    }
    throw new Exception\NotFoundException(
    Str\format('Identifier "%s" is not binding.', $id),
    );
    }
    nullͰͳ͍͔Ͳ͏͔

    View Slide

  85. <<__Rx>>
    protected function resolve(classname $id): T {
    if ($this->has($id)) {
    list($scope, $callable) = $this->map[$id];
    if ($callable is nonnull) {
    if ($scope === Scope::SINGLETON) {
    return $this->shared($id);
    }
    return $callable($this);
    }
    }
    throw new Exception\NotFoundException(
    Str\format('Identifier "%s" is not binding.', $id),
    );
    }
    γϯάϧτϯͷ৔߹͸ɺ
    ϝϞԽ

    View Slide

  86. ݎ࣮ͳΠϯελϯεऔಘ
    • ෆ࣮֬ͳཁૉ͕ೖΓࠐ·ͳ͍Α͏ͳ੍໿
    • Ϋϥεऔಘͱొ࿥Ͱଘࡏ͠ͳ͍஋Λར༻ͤ͞ͳ͍
    • ΦϒδΣΫτҎ֎Λѻ͏৔߹͸ɺ

    ͦΕ༻ͷొ࿥ɾऔಘϝιουΛ༻ҙ͢Δͷ͕࣮֬
    • PHPͰ͸੍ݶͰ͖ͳ͍͜ͱ͕PHPϥΠΫͳίʔυͰՄ

    View Slide

  87. HackͰ࡞ΔΞϓϦέʔγϣϯઃܭͬͯʁ
    • ϨΠϠʔυ΍ɺΫϦʔϯΞʔΩςΫνϟɺ

    ϔΩαΰφϧΞʔΩςΫνϟͳͲ

    ΦϒδΣΫτࢦ޲Ͱ༻͍ΒΕΔ΋ͷ͸౰વՄೳ
    • ෆ࣮֬ͳ஋΍ૢ࡞͕ೖΓࠐ·ͳ͍Α͏ʹ͢Δ͜ͱʹ

    ప͢Δ
    • ͍ΘΏΔ൚༻ੑ΍ݟ΍͢͞ɺΑΓ΋খ͘͞ɺ

    ͦͯ͠TypecheckerʹܕΛڭ͑Δίʔυʹ͢Δ

    View Slide

  88. Trait And Interface Requirements

    View Slide

  89. Traitͷ՝୊
    • PHPͷTrait͸੍໿Λ͔͚ΒΕͳ͍ͨΊɺ

    ෳࡶͳॲཧ͕͏·Ε΍͍͢
    • ϑϨʔϜϫʔΫͳͲʹ΋͜͏ͨ͠Trait͸ଟ͍

    View Slide

  90. protected function validateEmail(Request $request)
    {
    $this->validate(
    $request,
    ['email' => 'required|email']
    );
    }

    View Slide

  91. Laravelͷྫ: Illuminate\Foundation\Auth\SendsPasswordResetEmails
    • TraitʹύεϫʔυϦηοτʹؔ͢ΔৼΔ෣͍͕͋Δ
    • ྫʹڍ͛ͨvalidateϝιου͸ɺ

    ͜ͷτϨΠτʹ͸ͳ͍ϝιουͰɺ

    ଞͷΫϥεɾτϨΠτͷϝιουʹґଘ
    • Traitࣗମʹґଘؔ܎͕ͳ͍ͨΊɺ

    ಺༰Λ஌Βͳ͍ͱར༻Ͱ͖ͳ͍ྫ

    View Slide

  92. Traitʹ஫ҙ
    • ܧঝͰ͸ͳ͍ͨΊɺ؆୯ʹ࠶ར༻͕Ͱ͖Δ

    ͕ɺಛఆͷΫϥεͷϓϩύςΟ͕͋Δʁʂ
    • ϓϩύςΟ͕ଘࡏ͢Δ͔Ͳ͏͔

    νΣοΫ͍ͯ͠Δॲཧ͕͋Δʁʂ
    • άϩʔόϧΞΫηε͢ΔΑ͏ͳϔϧύʔؔ਺͕͋Δʁʂ

    View Slide

  93. /**
    * Fire an event when a lockout occurs.
    *
    * @param \Illuminate\Http\Request $request
    * @return void
    */
    protected function fireLockoutEvent(Request $request)
    {
    event(new Lockout($request));
    }
    /**
    * Get the rate limiter instance.
    *
    * @return \Illuminate\Cache\RateLimiter
    */
    protected function limiter()
    {
    return app(RateLimiter::class);
    }

    View Slide

  94. ෳࡶͳґଘؔ܎
    • Laravelͷ app ϔϧύʔؔ਺͸

    αʔϏείϯςφ(DIίϯςφ)ʹΞΫηε͕Ͱ͖Δ΋ͷ
    • τϨΠτͱͷґଘؔ܎͕͋ΔԿ͔ͷॲཧͷதͰɺ

    Πϯελϯεੜ੒ʹ͍ͭͯͷอূ͕Ͱ͖ͳ͍ͨΊɺ

    DIίϯςφʹάϩʔόϧΞΫηεΛ

    ߦ͍ΠϯελϯεΛऔಘ͢Δɺ

    ͱ͍͏αʔϏεϩέʔλґଘΛӅṭ

    View Slide

  95. HackͷTrait
    • τϨΠτΛར༻͢ΔΫϥεͷൣғΛࢦఆͰ͖Δ
    • ͋ΔΫϥεΛܧঝͨ͠΋ͷͷΈར༻ͤ͞Δ͔ɺ

    ͋ΔΠϯλʔϑΣʔεΛ࣮૷ͨ͠΋ͷͷΈ͔
    • ෳࡶʹͳΓ΍͍͢ґଘؔ܎Λੜ·ͳ͍Α͏ʹ

    ڧ੍తʹ੍໿Λ͔͚Δ͜ͱ͕Մ

    View Slide

  96. namespace Acme;
    class Request {
    }

    View Slide

  97. namespace Acme;
    interface RequestInterface {
    }

    View Slide

  98. namespace Acme;
    trait MessageTrait {
    require implements RequestInterface;
    private dict> $headers = dict[];
    }

    View Slide

  99. namespace Acme;
    trait MessageTrait {
    require implements RequestInterface;
    private dict> $headers = dict[];
    }
    ࢦఆͨ͠ΠϯλʔϑΣʔεΛ࣮૷
    ͨ͠ΫϥεͷΈʹར༻Λ੍ݶ

    View Slide

  100. src/Request.php:6:7,18: Failure to satisfy requirement:
    Acme\RequestInterface (Typing[4111])
    src/MessageTrait.php:6:22,37: Required here
    ΠϯλʔϑΣʔεΛ࣮૷͍ͯ͠ͳ͍
    Ϋϥεʹهड़ͨ͠৔߹͸ɺ
    ΤϥʔͱͳΓ·͢

    View Slide

  101. HackͷTrait
    • ར༻͢ΔΫϥεͷϝιουΛݺͿ͜ͱ͸Ͱ͖ͳ͍
    • method_exists͸ܧঝݩͷ΋ͷ͔ɺ

    ·ͨ͸ಉҰͷτϨΠτ಺ͷΈҎ֎Ͱ͸

    TypececkerͰΤϥʔ

    View Slide

  102. namespace Acme;
    class Request implements RequestInterface {
    use MessageTrait;
    protected function getVersion(): string {
    return '1.1';
    }
    }

    View Slide

  103. trait MessageTrait {
    require implements RequestInterface;
    private dict> $headers = dict[];
    public function castIntVersion(): int {
    return (int) $this->getVersion();
    }
    }

    View Slide

  104. trait MessageTrait {
    require implements RequestInterface;
    private dict> $headers = dict[];
    public function castIntVersion(): int {
    return (int) $this->getVersion();
    }
    }
    ϦΫΤετΫϥεͷϝιουΛ
    ར༻͍ͨ͠

    View Slide

  105. src/MessageTrait.php:10:25,34: Could not find method getVersion
    in an object of type Acme\MessageTrait (Typing[4053])
    src/MessageTrait.php:10:18,22: This is why I think it is an
    object of type Acme\MessageTrait
    src/MessageTrait.php:5:7,18: Declaration of Acme\MessageTrait
    is here
    ΠϯλʔϑΣʔεʹఆٛ͞Ε͍ͯͳ͍
    ϝιου͸ίʔϧෆՄ

    View Slide

  106. HackͷTrait
    • ಛఆͷΫϥε͕ར༻͢Δ

    ΦϒδΣΫτͷϓϩύςΟ͕͋Δ৔߹͸ɺ

    Πϯελϯε͕ਖ਼͍͠ΦϒδΣΫτͰ͋Δ͔Ͳ͏͔Λ

    هड़͠ͳ͚Ε͹ͳΒͳ͍

    View Slide

  107. namespace Acme;
    trait MessageTrait {
    require implements RequestInterface;
    private dict> $headers = dict[];
    protected \stdClass $class;
    public function getStdClass(): \stdClass {
    return $this->class;
    }
    }

    View Slide

  108. namespace Acme;
    trait MessageTrait {
    require implements RequestInterface;
    private dict> $headers = dict[];
    protected \stdClass $class;
    public function getStdClass(): \stdClass {
    return $this->class;
    }
    }
    stdClassΛϓϩύςΟͱͯ͠ѻ͏৔߹

    View Slide

  109. namespace Acme;
    class Request implements RequestInterface {
    use MessageTrait;
    protected function getClass(): string {
    return \strval($this->class);
    }
    }
    Traitʹهड़͞ΕͨϓϩύςΟΛͦͷ··
    ར༻͢ΔͱΤϥʔ

    View Slide

  110. namespace Acme;
    class Request implements RequestInterface {
    use MessageTrait;
    public function __construct(
    protected \stdClass $class
    ) {}
    protected function getClass(): string {
    return \strval($this->class);
    }
    }
    ࣮֬ʹΠϯελϯεΛ౉͢͜ͱ

    View Slide

  111. HackͷTrait
    • ൚༻ੑΛ࣋ͭTraitͱͨ͠৔߹ɺ

    ͋ΔΫϥεͰ͸࢖Θͳ͍ϓϩύςΟ͕

    ଘࡏ͢Δ৔߹͸Type Error͕ൃੜ͢Δ

    ෆ࣮֬ͳࢦఆ͸Ͱ͖ͳ͍
    • ͜ͷϓϩύςΟͲ͜Ͱ࢖ΘΕ͍ͯΔͷʁ

    ͱ͍͏࣮૷͕ੜ·Εʹ͘͘ɺ໾ׂ͕໌֬

    View Slide

  112. Implementing DDD
    Generic Repository
    *ଟগΦʔόʔͳྫ

    View Slide

  113. https://github.com/ytake/hack-ddd-repository-sample
    *αϯϓϧίʔυ͸ͪ͜ΒΛ͝ཡ͍ͩ͘͞

    View Slide

  114. Generic Repository
    • GenericsͱRepository PatternΛ૊Έ߹Θͤͯɺ

    ୯Ұͷू໿ʹؔ࿈͚ͮΔ
    • SpecificationύλʔϯΛ૊Έ߹ΘͤΔ͜ͱͰখ͘͞͠

    نଇΛద༻͢Δ
    • ଞݴޠͰ͸Α͘ར༻͞ΕΔύλʔϯ

    HackͰ΋ಉ͜͡ͱ͕Ͱ͖Δʂ

    View Slide

  115. ࢦఆͨ͠೔෇ΑΓ
    ৽͍͠هࣄ͕ಡΈ͍ͨ

    View Slide

  116. هࣄΦϒδΣΫτ
    *DDDࣗମͷ࿩Λ͢ΔΘ͚Ͱ͸ແ͍ͷͰৄࡉ͸ׂѪ

    View Slide

  117. namespace Acme\Domain\Model;
    abstract class Identifier {
    public function __construct(
    private T $id
    ) {}
    <<__Rx>>
    public function id(): T {
    return $this->id;
    }
    <<__Rx>>
    public function equals(Identifier $id): bool {
    return $this->id === $id->id();
    }
    }

    View Slide

  118. namespace Acme\Domain\Model;
    abstract class Identifier {
    public function __construct(
    private T $id
    ) {}
    <<__Rx>>
    public function id(): T {
    return $this->id;
    }
    <<__Rx>>
    public function equals(Identifier $id): bool {
    return $this->id === $id->id();
    }
    }
    Ҿ਺΍໭ΓͰར༻͢ΔܕΛҰͭʹ

    View Slide

  119. namespace Acme\Domain\Model;
    abstract class Identifier {
    public function __construct(
    private T $id
    ) {}
    <<__Rx>>
    public function id(): T {
    return $this->id;
    }
    <<__Rx>>
    public function equals(Identifier $id): bool {
    return $this->id === $id->id();
    }
    }
    int
    int
    int
    int

    View Slide

  120. namespace Acme\Domain\Model\Article;
    use namespace Acme\Domain\Model;
    final class ArticleId extends Model\Identifier {
    }

    View Slide

  121. namespace Acme\Domain\Model\Article;
    use namespace Acme\Domain\Model;
    final class ArticleId extends Model\Identifier {
    }
    ྫ͑͹ɺIdͱ͍ͬͯ΋

    intͱ͸ݶΒͳ͍

    View Slide

  122. namespace Acme\Domain\Model;
    interface EntityInterface {
    public function getID(): T;
    }

    View Slide

  123. namespace Acme\Domain\Model\Article\Entity;
    use type DateTime;
    use type Acme\Domain\Model\EntityInterface;
    use type Acme\Domain\Model\Article\ArticleId;
    use type Acme\Domain\Model\Article\Body;
    class Article implements EntityInterface {
    const int EXPIRE_EDIT_TIME = 120;
    public function __construct(
    private ArticleId $id,
    private Body $body,
    private DateTime $createdAt = new DateTime()
    ) {}
    <<__Rx>>
    public function getID(): T {
    return $this->id->id();
    } // লུ
    }

    View Slide

  124. namespace Acme\Domain\Model\Article\Entity;
    use type DateTime;
    use type Acme\Domain\Model\EntityInterface;
    use type Acme\Domain\Model\Article\ArticleId;
    use type Acme\Domain\Model\Article\Body;
    class Article implements EntityInterface {
    const int EXPIRE_EDIT_TIME = 120;
    public function __construct(
    private ArticleId $id,
    private Body $body,
    private DateTime $createdAt = new DateTime()
    ) {}
    <<__Rx>>
    public function getID(): T {
    return $this->id->id();
    } // লུ
    }
    ୯ҰͷܕͷΈ
    ޡͬͨܕ͕հೖͮ͠Β͍

    (TypecheckerͰݕ஌)

    View Slide

  125. ϦϙδτϦ
    *DDDࣗମͷ࿩Λ͢ΔΘ͚Ͱ͸ແ͍ͷͰৄࡉ͸ׂѪ

    View Slide

  126. T?
    • EntityInterfaceΛܕએݴͰར༻͢Δ͜ͱ΋OK
    • ͨͩ͠EntityInterfaceΛ࣮૷͍ͯ͠Δ΋ͷͰ͋Ε͹

    ͳΜͰ΋Α͍͜ͱʹͳͬͯ͠·͏

    • ΑΓڧ੍͍໿Λ͔͚͍ͨ

    View Slide

  127. interface ArticleRepositoryInterface {
    public function add(T $entity): void;
    public function remove(T $entity): void;
    public function findById(TId $id): T;
    public function latestArticles(DateTime $date): Mappublic function query(
    SpecificationInterface $specification
    ): Map;
    public function size(): int;
    }

    View Slide

  128. interface ArticleRepositoryInterface {
    public function add(T $entity): void;
    public function remove(T $entity): void;
    public function findById(TId $id): T;
    public function latestArticles(DateTime $date): Mappublic function query(
    SpecificationInterface $specification
    ): Map;
    public function size(): int;
    }
    TId : Identifier͸intಛఆͷܕ͔Ͳ͏͔͸ɻɻʁ
    T: EntityͰ͸͋Δ͕ɺ͜ͷϦϙδτϦͰѻ͏΋ͷ͸શͯಉ͡ܕ

    View Slide

  129. ϦϙδτϦ ࣮૷
    *DDDࣗମͷ࿩Λ͢ΔΘ͚Ͱ͸ແ͍ͷͰৄࡉ͸ׂѪ

    View Slide

  130. abstract class BaseRepository>
    implements ArticleRepositoryInterface {
    protected Map $collect = Map{};
    public function add(T $article): void {
    $this->collect->add(Pair{$article->getID(), $article});
    }
    // লུ
    <<__Rx>>
    public function findById(TId $id): T {
    if($this->collect->contains($id)) {
    return $this->collect->at($id);
    }
    throw new \RuntimeException('Not Found.');
    }
    }

    View Slide

  131. abstract class BaseRepository>
    implements ArticleRepositoryInterface {
    protected Map $collect = Map{};
    public function add(T $article): void {
    $this->collect->add(Pair{$article->getID(), $article});
    }
    // লུ
    <<__Rx>>
    public function findById(TId $id): T {
    if($this->collect->contains($id)) {
    return $this->collect->at($id);
    }
    throw new \RuntimeException('Not Found.');
    }
    }
    TId : Identifier͸intಛఆͷܕ͔Ͳ͏͔͸ɻɻʁ
    T: EntityInterfaceΛ࣮૷ͯ͠Δ͕ɺTIdࢦఆ͞Εͨ΋ͷ

    View Slide

  132. abstract class BaseRepository>
    implements ArticleRepositoryInterface {
    protected Map $collect = Map{};
    public function add(T $article): void {
    $this->collect->add(Pair{$article->getID(), $article});
    }
    // লུ
    <<__Rx>>
    public function findById(TId $id): T {
    if($this->collect->contains($id)) {
    return $this->collect->at($id);
    }
    throw new \RuntimeException('Not Found.');
    }
    }
    ίϨΫγϣϯ͸ɺ ͷϚοϓʹ
    Ұ؏ੑ͕͋Γɺ͜ΕҎ֎ͷ΋ͷ͸ར༻͞Εͳ͍

    View Slide

  133. namespace Acme\Infrastructure\Persistence\Map;
    use type DateTime;
    use type Acme\Domain\Model\Article\Entity\Article;
    class ArticleRepository
    extends BaseRepository> {
    <<__Rx>>
    public function latestArticles(
    DateTime $date
    ): Map> {
    return $this->collect->filter(
    $v ==> $v->createdAt() > $date
    );
    }
    }

    View Slide

  134. namespace Acme\Infrastructure\Persistence\Map;
    use type DateTime;
    use type Acme\Domain\Model\Article\Entity\Article;
    class ArticleRepository
    extends BaseRepository> {
    <<__Rx>>
    public function latestArticles(
    DateTime $date
    ): Map> {
    return $this->collect->filter(
    $v ==> $v->createdAt() > $date
    );
    }
    }
    ͲΜͳܕΛར༻͢Δͷ͔ɺ۩৅ΫϥεͰ໌ه

    View Slide

  135. namespace Acme\Infrastructure\Persistence\Map;
    use type DateTime;
    use type Acme\Domain\Model\Article\Entity\Article;
    class ArticleRepository
    extends BaseRepository> {
    <<__Rx>>
    public function latestArticles(
    DateTime $date
    ): Map> {
    return $this->collect->filter(
    $v ==> $v->createdAt() > $date
    );
    }
    } ಛఆͷू໿ݶఆͷॲཧͳͲ͕͋Ε͹ɺ

    ΠϯλʔϑΣʔε௥Ճ΍ɺઐ༻ͷॲཧͷΈهड़

    View Slide

  136. namespace Acme\Infrastructure\Persistence\Map;
    use type Acme\Domain\Model\Bookmark\Entity\Bookmark;
    class BookmarkRepository
    extends BaseRepository> {
    }
    ྫ͑͹ར༻͢Δܕ͚ͩΛม͑Δ
    ू໿ͷૢ࡞ͱͯ͠͸͜Ε͚ͩͰ׬݁

    ৄࡉͳॲཧ͸࢓༷ύλʔϯͳͲʹ

    View Slide

  137. ࢓༷
    *DDDࣗମͷ࿩Λ͢ΔΘ͚Ͱ͸ແ͍ͷͰৄࡉ͸ׂѪ

    View Slide

  138. ࢓༷ύλʔϯ
    • ΞϓϦέʔγϣϯαʔϏεͳͲ͔Β

    ϦϙδτϦͱڞʹར༻͞ΕΔ
    • DBΞΫηε΍Կ͔͠Βͷ֎෦ϦιʔεΛ࢖͏΋ͷ΋

    ࢓༷ͱͯ͠੾Γग़͢

    View Slide

  139. namespace Acme\Domain\Model\Article\Specification;
    interface SpecificationInterface {
    <<__Rx>>
    public function isSatisfiedBy(T $entity): bool;
    }
    ࢓༷Λຬͨ͢ͷ͔Ͳ͏͔

    View Slide

  140. ࢓༷ ࣮૷
    *DDDࣗମͷ࿩Λ͢ΔΘ͚Ͱ͸ແ͍ͷͰৄࡉ͸ׂѪ

    View Slide

  141. class LatestPostSpecification
    implements SpecificationInterface> {
    public function __construct(
    private DateTime $since
    ) {}
    <<__Rx>>
    public function isSatisfiedBy(
    Article $article
    ): bool {
    return $article->createdAt() > $this->since;
    }
    }
    ͜Ε·Ͱͷྫͱಉ༷ʹ۩৅Ϋϥεʹ

    ࣮ࡍʹద༻ͤ͞ΔܕΛهड़

    View Slide

  142. class LatestPostSpecification
    implements SpecificationInterface> {
    public function __construct(
    private DateTime $since
    ) {}
    <<__Rx>>
    public function isSatisfiedBy(
    Article $article
    ): bool {
    return $article->createdAt() > $this->since;
    }
    }
    ࢦఆ͞Εͨ೔࣌ΑΓ΋৽͍͠هࣄͳΒ

    ࢓༷͕ຬͨ͞Ε·͢Α

    View Slide

  143. <<__Rx>>
    public function query(
    SpecificationInterface $specification
    ): Map {
    return $this->collect->filter(
    $v ==> $specification->isSatisfiedBy($v)
    );
    }
    ࢓༷ɾϦϙδτϦͱ૊Έ߹ΘͤΔ

    View Slide

  144. ΞϓϦέʔγϣϯαʔϏε

    View Slide

  145. use type Vendor\Path\ArticleRepositoryInterface as Repository;
    use type Vendor\Path\\ArticleSpecificationFactoryInterface as
    SpecificationFactory;
    final class LatestArticleFeed {
    public function __construct(
    private Repository> $repository,
    private SpecificationFactory> $specification
    ) {
    } ࢦఆͨ͠ܕΛར༻͢ΔΑ͏ʹద༻͞Εͨ
    ΠϯλʔϑΣʔεΛ࣮૷ͨ͠΋ͷΛར༻͍ͯͩ͘͠͞

    View Slide

  146. public function execute(
    FeedRequestTransfer $request
    ): vec'id' => int,
    'content' => string,
    'created_at' => \DateTime)> {
    $result = $this->repository->query(
    $this->specificationFactory
    ->createLatestPosts($request->getDateTime()
    ));
    if(C\count($result)) {
    return Vec\map($result, ($v) ==> {
    return shape(
    'id' => $v->getID(),
    'content' => $v->body()->content(),
    'created_at' => $v->createdAt()
    );
    });
    }
    return vec[];
    }
    ࢓༷Λຬͨ͢هࣄΦϒδΣΫτΛϦϙδτϦ͔Βऔಘ

    View Slide

  147. public function execute(
    FeedRequestTransfer $request
    ): vec'id' => int,
    'content' => string,
    'created_at' => \DateTime)> {
    $result = $this->repository->query(
    $this->specificationFactory
    ->createLatestPosts($request->getDateTime()
    ));
    if(C\count($result)) {
    return Vec\map($result, ($v) ==> {
    return shape(
    'id' => $v->getID(),
    'content' => $v->body()->content(),
    'created_at' => $v->createdAt()
    );
    });
    }
    return vec[];
    }
    Collectionૢ࡞

    ϦϙδτϦ͔Βऔಘ͞ΕͨΦϒδΣΫτΛҰׅૢ࡞
    *͜͜Ͱ͸shapeͷΈͷvec഑ྻʹ

    View Slide

  148. ΫϥΠΞϯτ

    View Slide

  149. ܕએݴ͞ΕͨΠϯλʔϑΣʔεΛຬͨ͢

    ۩৅ΫϥεͷΠϯελϯε
    $service = new Application\Service\LatestArticleFeed(
    new Map\ArticleRepository(),
    new Map\ArticleSpecificationFactory()
    );
    $result = $service->execute(
    new Application\FeedRequestTransfer([
    'datetime' => new DateTime('-4 hours'),
    ])
    );

    View Slide

  150. $service = new Application\Service\LatestArticleFeed(
    new Map\ArticleRepository(),
    new Map\ArticleSpecificationFactory()
    );
    $result = $service->execute(
    new Application\FeedRequestTransfer([
    'datetime' => new DateTime('-4 hours'),
    ])
    );
    ϑΥʔϜ΍APIͳͲ͔Β஋Λૹ৴

    View Slide

  151. final class FeedRequestTransfer {
    const type FeedRequest = shape(
    'datetime' => DateTime
    );
    public function __construct(
    private array $request
    ) {}
    public function getDateTime(): DateTime {
    $request = $this->request as this::FeedRequest;
    return $request['datetime'];
    }
    }
    Ұൠతͳ഑ྻΛड͚औΔ

    View Slide

  152. final class FeedRequestTransfer {
    const type FeedRequest = shape(
    'datetime' => DateTime
    );
    public function __construct(
    private array $request
    ) {}
    public function getDateTime(): DateTime {
    $request = $this->request as this::FeedRequest;
    return $request['datetime'];
    }
    }
    ShapeͰ഑ྻʹରͯ͠
    ظ଴͢ΔϑΟʔϧυͱܕఆٛ

    View Slide

  153. final class FeedRequestTransfer {
    const type FeedRequest = shape(
    'datetime' => DateTime
    );
    public function __construct(
    private array $request
    ) {}
    public function getDateTime(): DateTime {
    $request = $this->request as this::FeedRequest;
    return $request['datetime'];
    }
    } ϑΟʔϧυͱܕݕࠪ
    OKͳΒͦͷ··഑ྻ
    NGͳΒType Error͕εϩʔ

    View Slide

  154. ·ͱΊ
    • Hack͸े෼঎༻ΞϓϦέʔγϣϯͰ׆༻
    • ݎ͘࡞ΔɺܕΛҙࣝ͢Δ
    • Կࣄ΋΍Γ͗͢ʹ஫ҙ
    • ͦͷPHPΞϓϦέʔγϣϯɺ

    HackͰॻ͍ͯΈ·ͤΜ͔ʁ

    View Slide