Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
クラシコムとLaravelとDDD
Search
Takeru Hirose
September 13, 2018
Programming
8
9.1k
クラシコムとLaravelとDDD
Laravel Meetup Tokyo Vol.11
https://laravel-meetup-tokyo.connpass.com/event/93838/
Takeru Hirose
September 13, 2018
Tweet
Share
More Decks by Takeru Hirose
See All by Takeru Hirose
Rails と Laravel どちらを選ぶべきか? / Rails or Laravel
takeru0757
0
3.2k
開発・CI・運用におけるDocker戦略(クラシコムの場合)/ Dockerising Apps at Kurashicom
takeru0757
1
3.7k
SimpleとEasyは違う / Simple is not Easy
takeru0757
95
46k
クラウドワークスとデザインガイドライン
takeru0757
2
1.7k
ユーザーのためのデザイン。ユーザーのためのデザイン組織。
takeru0757
4
4.9k
デザイナーとデザイナーじゃない人でデザイン組織を作る
takeru0757
4
3.6k
受託会社とサービス運営会社におけるUXの考え方の違い
takeru0757
1
2.6k
Other Decks in Programming
See All in Programming
Leading Effective Engineering Teams in the AI Era
addyosmani
7
540
アメ車でサンノゼを走ってきたよ!
s_shimotori
0
230
『毎日の移動』を支えるGoバックエンド内製開発
yutautsugi
2
270
AI駆動で0→1をやって見えた光と伸びしろ
passion0102
1
650
EMこそClaude Codeでコード調査しよう
shibayu36
0
270
overlayPreferenceValue で実現する ピュア SwiftUI な AdMob ネイティブ広告
uhucream
0
200
When Dependencies Fail: Building Antifragile Applications in a Fragile World
selcukusta
0
110
Google Opalで使える37のライブラリ
mickey_kubo
2
120
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
520
Catch Up: Go Style Guide Update
andpad
0
240
Writing Better Go: Lessons from 10 Code Reviews
konradreiche
2
4.8k
なぜGoのジェネリクスはこの形なのか? - Featherweight Goが明かす設計の核心
qualiarts
0
200
Featured
See All Featured
The World Runs on Bad Software
bkeepers
PRO
72
11k
BBQ
matthewcrist
89
9.8k
Building an army of robots
kneath
306
46k
Navigating Team Friction
lara
190
15k
Designing Experiences People Love
moore
142
24k
Leading Effective Engineering Teams in the AI Era
addyosmani
7
540
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.6k
Context Engineering - Making Every Token Count
addyosmani
7
280
Done Done
chrislema
185
16k
Git: the NoSQL Database
bkeepers
PRO
431
66k
jQuery: Nuts, Bolts and Bling
dougneiner
65
7.9k
Transcript
ኍ݈ʢגࣜձࣾΫϥγίϜʣ -BSBWFM.FFUVQ5PLZP7PM ΫϥγίϜͱ-BSBWFMͱ%%%
ɺѪݝੜ·Εɻ݄ɺגࣜձࣾΫϥγίϜʹೖࣾɻ લ৬·Ͱ3VCZPO3BJMTͰͷΞϓϦέʔγϣϯ։ൃɺσβΠϯ৫ ͷ্ཱͪ͛ͳͲʹܞΘΔɻݱࡏ-BSBWFMͰͷΞϓϦέʔγϣϯ։ൃ ͷ΄͔ɺσβΠφʔ࠾༻ʹऔΓΉɻ ኍ݈5BLFSV)JSPTF!UBLFSV
w &$ ϝσΟΞͷΑ͏ͳαΠτ w ݄ؒ17 ສ w ݄ؒ66ສ w ࣾͰ։ൃɾӡ༻
ΫϥγίϜʮԤɺΒ͠ͷಓ۩ళʯΛӡӦ IPLVPILVSBTIJDPN
-BSBWFM %%%ʢΫϥγίϜͷ߹ʣ
3BJMT͔Β-BSBWFMͷॻ͖͑ϓϩδΣΫτ ϖʔδ Χʔτʢܾࡁʣ -BSBWFM %%%Λ࠾༻
ۀࣝΛίʔυͰදݱ ʢ-BSBWFMʹͰ͖Δ͚ͩґଘ͠ͳ͍ʣ -BZFSFE"SDIJUFDUVSF 1SFTFOUBUJPO-BZFS *OGSBTUSVDUVSF-BZFS "QQMJDBUJPO-BZFS %PNBJO-BZFS -BSBWFMͷػೳΛ࠷େݶʹ׆༻
w ϢϏΩλεݴޠΫϥεʢܕʣʢ&OUJUZ 7BMVF0CKFDU FUDʣ w ྫʣ"QQa%PNBJOa.PEFMTa6TFSa&OUJUZ w ֤Ϋϥε1MBJO0ME1)10CKFDU 1010
w &OUJUZͰ͋ͬͯ&MPRVFOUa.PEFMΛܧঝ͠ͳ͍ʢ&MPRVFOUͱผʣ w ίϯετϥΫλʹΑͬͯෆม݅ΛνΣοΫ͢Δ w ༷ʹ͍ͯ͠Εྫ֎ʢΠϯελϯεΛ࡞Δ͜ͱ͕Ͱ͖ͳ͍ʣ ۀࣝʢυϝΠϯϩδοΫʣΛίʔυͰදݱ
ྫʣ߹ܭֹۚͷܭࢉ ୯Ձ จྔ খܭ ߹ܭ ૹྉ
ྫʣ߹ܭֹۚͷܭࢉ ୯Ձ Product\UnitPrice จྔ Order\Quantity খܭ Order\SubTotal ߹ܭ Order\Total ૹྉ
Shipping\Fee
declare(strict_types=1); final class Product\UnitPrice { public function __construct(int $value) {
if ($value < 0) { throw new \InvalidArgumentException(); } $this->value = $value; } } ྫʣ୯Ձ WBMVFͷʮ୯Ձʯ࡞Εͳ͍ WBMVFඞͣJOU
declare(strict_types=1); final class Order\Quantity { public function __construct(int $value) {
if ($value < 1) { throw new \InvalidArgumentException(); } $this->value = $value; } } ྫʣจྔ WBMVFͷʮจྔʯ࡞Εͳ͍ WBMVFඞͣJOU
declare(strict_types=1); final class Order\SubTotal { public function __construct( Product\UnitPrice $unitPrice,
Order\Quantity $quantity ) { $this->value = $unitPrice->value() * $quantity->value(); } } ྫʣখܭ୯Ձ จྔ ʮখܭʯඞͣʮ୯Ձʯͱʮจྔʯ͔Β࡞ΒΕΔ
declare(strict_types=1); final class Shipping\Method { public function calculateFee(Order\SubTotal $subTotal): Shipping\Fee
{ if ($subTotal->value() >= self::IS_FREE) { return new Shipping\Fee(0); } return new Shipping\Fee(self::REGULAR_FEE); } } ྫʣখܭ͕ҰఆֹۚҎ্ͷ߹ૹྉ͕ແྉ ඞͣʮখܭʯΛ༻͍ͯܭࢉ͞ΕΔ ඞͣʮૹྉʯΛฦ͢
declare(strict_types=1); final class Order\Total { public function __construct( Shipping\Method $shippingMethod,
Order\SubTotal $subTotal ) { $shippingFee = $shippingMethod->calculateFee($subTotal); $this->value = $subTotal->value() + $shippingFee->value(); } } ྫʣ߹ܭখܭ খܭΛجʹܭࢉ͞Εͨૹྉ ༷Ҏ֎ͷํ๏Ͱʮ߹ܭʯΛ࡞Δ͜ͱ͕Ͱ͖ͳ͍
w ۀࣝΛݴޠ༷ʹΑͬͯදݱͰ͖Δʢڧ੍Ͱ͖Δʣ w ҾɾฦΓͷܕࢦఆ͕ͦͷ··ͦͷ֓೦ͷ༷ɾؔੑΛදݱ w ༷ʹ͍ͯ͠Ε࣮ߦͰ͖ͳ͍˰όάͷݮɾૣظൃݟ w ༷ʢϩδοΫʣ͕Ϋϥεʹแ͞Ε͍ͯΔͷͰࢄҳ͠ͳ͍ w ফඅ੫ͱ͔ʜʢ੫ࠐՁ֨
ྔPS੫ൈՁ֨ ྔ ੫ʣ w ༷ʹͨ͠Πϯελϯε͕ଘࡏ͠ಘͳ͍ͷͰඞཁͳςετݮΔ ϢϏΩλεݴޠΫϥεʢܕʣ
ҾɾฦΓͷؒҧ͍˰5ZQF&SSPS ݴޠ༷ʹΑͬͯදݱͰ͖Δͱ͍͏͜ͱ ༷ʹ͢Δ߹˰-PHJD&YDFQUJPO ➔ྫ֎͕ى͖ͳ͍Α͏ʹ࣮͢Ε͍͍ͩͨେৎ l͜ͷྨͷྫ֎͕ग़ͨ߹ɺ͕ࣗॻ͍ ͨίʔυΛमਖ਼͖͢Ͱ͢ɻz
-BSBWFMͱ%%%ͷ౷߹
%PNBJO-BZFS⾩*OGSBTUSVDUVSF-BZFS 1SFTFOUBUJPO-BZFS *OGSBTUSVDUVSF-BZFS "QQMJDBUJPO-BZFS %PNBJO-BZFS
w 3FQPTJUPSZ1BUUFSO w &OUJUZࣗӬଓԽʹؔ͢ΔϝιουΛ࣋ͨͳ͍ w 3FQPTJUPSZ͔Βऔಘˠมߋˠ3FQPTJUPSZʹ͢͜ͱͰอଘ w 3FQPTJUPSZͰ&MPRVFOU⾩&OUJUZΛ૬ޓม w 3FQPTJUPSZ͕֎෦ͱΓͱΓ͢Δ%PNBJO-BZFSͷΫϥεʢܕʣ
w &MPRVFOU03.ͩͱׂΓΔʢରͰରԠ͍ͯ͠ΔͱݶΒͳ͍ʣ w 3FQPTJUPSZ&MPRVFOUʹͰ͖Δ͚ͩۀࣝΛ࣋ͨͤͳ͍ &OUJUZͷӬଓԽ
declare(strict_types=1); final class UserRepository implements User\Repository { public function find(User\Id
$id): User\Entity { $record = UserEloquent::find($id->value()); $entity = ...; return $entity; } public function store(User\Entity $entity): void { $record = ...; $record->save(); } } ྫʣ6TFS3FQPTJUPSZ JOUFSGBDF%PNBJO-BZFSʹஔˠ4FSWJDF1SPWJEFSʹొ &MPRVFOUˠ&OUJUZʹม &OUJUZˠ&MPRVFOUʹม
"QQMJDBUJPO-BZFS⾩%PNBJO-BZFS 1SFTFOUBUJPO-BZFS *OGSBTUSVDUVSF-BZFS "QQMJDBUJPO-BZFS %PNBJO-BZFS
w $POUSPMMFSͷׂ6*ͱ%PNBJO-BZFSͷؒΛऔΓ࣋ͭ͜ͱ w Ϣʔβ͔ΒͷೖྗΛ%PNBJO-BZFSͷ֓೦ʹม͢Δ w %PNBJO-BZFSͷ֓೦ΛϢʔβͷग़ྗʹม͢Δ w 7BMJEBUPSͷׂʮྫ֎Λൃੜͤ͞ͳ͍͜ͱʯ w ෆਖ਼ͳೖྗΛϢʔβʹ͔Γ͘͢ϑΟʔυόοΫ͢Δ͜ͱʹूத͢Δ
ʢσʔλͷ߹ੑ%PNBJO-BZFSҎԼʹΑͬͯ୲อ͞Ε͍ͯΔʣ w %PNBJO-BZFSΛʮ͏ଆʯ͕ࣄલ݅ɾෆม݅Λຬͨٛ͢Λෛ͏ ʢܖʹΑΔઃܭʣ $POUSPMMFS7BMJEBUPS
declare(strict_types=1); class UsersController extends Controller { public function show(int $id)
{ $user = app(User\Repository::class)->find(new User\Id($id)); // [...] } public function store(StoreRequest $request) { $data = $request->validate(); $user = ...; app(User\Repository::class)->store($user); } } ྫʣ6TFST$POUSPMMFS "QQMJDBUJPO-BZFS%PNBJO-BZFSʹஔ͞ΕͨJOUFSGBDFʹґଘ͢Δ 7BMJEBUPSΛ௨աͨ͠ʹΑͬͯ&OUJUZΛੜ
w -BSBWFMͷ͍͍ͨͯͷػೳ*OUFSGBDFΛ࣮͍ͯ͠Ε͑Δ w Ϣʔβʔೝূʢ"VUIFOUJDBUJPO"VUIPSJ[BUJPOʣ6TFS.PEFM͕&MPRVFOU Ͱͳͯ͑͘Δ w -BSBWFMʹ༻ҙ͞Ε͍ͯΔ֦ுͷΈΛ࠷େݶʹ׆༻͢Δ w #MBEFͷ%JSFDUJWFΛ֦ு͢ΕWJFXʹ͓͍ͯ%PNBJO-BZFSͷΫϥεΛࣗ વʹ͑Δ
ͦͷଞ
ͬͯΈͯͲ͏͔ͩͬͨ
w ۀࣝʹର͢Δཧղ͕ਂ·Δʢ͔Βͳ͍ͱ࡞Εͳ͍ʣ w ͕ࣗϓϩδΣΫτʹೖͬͨͷೖࣾͯ͠ϲ݄ޙ͘Β͍ w Ҏલ͔Β͍ΔΤϯδχΞͱҰॹʹϞσϦϯάˠϞϒϓϩάϥϛϯά w ίʔυͰදݱ͠Α͏ͱ͢Δ͜ͱͰ༷͕໌շʹͳΔ w ͯ͢ͷ֓೦Λཧతʹهड़͢ΔͨΊᐆດͳ༷͕ଘࡏ͠ಘͳ͍
w ཧతʹໃ६༷͕ͨ͠ൃݟ͞Εͨ͜ͱ w ίʔυΛಡΊ༷͕͔Δˠߏஙޙۀࣝͷڞ༗͕༰қ ʮֶͼʯ͕ͳ͍
ʮֶͼʯ͕ͳ͍ Ϟϒϓϩάϥϛϯά ϞσϦϯά
w ۀࣝʢ࠷େࣄͰෳࡶͳ෦ʣʹؔ͢Δςετ͕γϯϓϧʹͳΔ w ୯७ͳςετͷੵΈॏͶʹΑ༷͕ͬͯकΒΕ͍ͯΔ͜ͱΛ୲อͰ͖Δ w པΓʹͳΔίʔυΧόϨοδɾ੩తղੳʢQIQTUBOʣ w ʮ࠷ѱͷࣄଶʯΛট͘ϦεΫΛݮͰ͖Δ w ݴޠ༷ͷ੍ʹΑΓۀ͔ࣝΒҳͨ͠هड़࣮ߦͰ͖ͳ͍
w ྫ֎͕ى͖ͳ͍Α͏ʹ࣮͢Ε͍͍ͩͨେৎʢ࠶ܝʣ ҆৺ײ͕ͳ͍
ྫʣখܭΛςετ͢Δ߹ ୯Ձ Product\UnitPrice จྔ Order\Quantity খܭ Order\SubTotal
class ProductUnitPriceTest extends TestCase { public function testConstruct(): void {
$expectedValue = $this->faker()->numberBetween(0); $unitPrice = new Product\UnitPrice($expectedValue); $this->assertEquals($expectedValue, $unitPrice->value()); } public function testConstructFail(): void { $this->expectException(\InvalidArgumentException::class); $invalidValue = $this->faker()->numberBetween(-2147483647, -1); new Product\UnitPrice($invalidValue); } } ྫʣ୯Ձͷςετ ਖ਼ৗܥʢWBMVFʣ ҟৗܥʢWBMVFʣ
class OrderQuantityTest extends TestCase { public function testConstruct(): void {
$expectedValue = $this->faker()->numberBetween(1); $quantity = new Order\Quantity($expectedValue); $this->assertEquals($expectedValue, $quantity->value()); } public function testConstructFail(): void { $this->expectException(\InvalidArgumentException::class); $invalidValue = $this->faker()->numberBetween(-2147483647, 0); new Order\Quantity($invalidValue); } } ྫʣจྔͷςετ ਖ਼ৗܥʢWBMVFʣ ҟৗܥʢWBMVFʣ
class OrderSubTotalTest extends TestCase { public function testConstruct(): void {
$unitPrice = new Product\UnitPrice($this->faker()->numberBetween(0)); $quantity = new Order\Quantity($this->faker()->numberBetween(1)); $expectedValue = $unitPrice->value() * $quantity->value(); $subTotal = new Order\SubTotal($unitPrice, $quantity); $this->assertEquals($expectedValue, $subTotal->value()); } } ྫʣখܭͷςετ ҾͱͳΔΦϒδΣΫτͷҟৗܥςετࡁΈ Ҿͷܕݴޠ༷ʹΑͬͯอূ͞Ε͍ͯΔˠͨͩͷֻ͚ࢉͷςετ
w ΄ͱΜͲͷذ͕୯७ w ˠ༰қʹཏͰ͖Δ w ˠཏͰ͖͍ͯΕ҆৺ w !DPWFSTΛͪΌΜͱॻ͘ • forceCoversAnnotation="true"
པΓʹͳΔίʔυΧόϨοδ
҆৺ײ͕ͳ͍ ੩తղੳʢQIQTUBOʣ ΧόϨοδʢ$PWFSBMMTʣ
w ϞσϦϯά͍͠ w Ϟσϧͷڥքͱ%#ͷτϥϯβΫγϣϯύϑΥʔϚϯεͱͷ݉Ͷ߹͍ w Ϟσϧͷ߹ੑͱ6*ͷॊೈੑͱͷ݉Ͷ߹͍ w ྫʣૹઌ͕ܾఆ͞Ε͍ͯͳ͍ͷʹૹྉΛද͍ࣔͨ͠ w ͦͦ-BSBWFMʢ1)1ʣͰΔ͖ͩͬͨͷ͔ʁ
w 1)1ͰܕΛѻ͏ݴޠͱͯ͠·ͩ·ͩඇྗʢδΣωϦοΫͱ͔ʣ w ͬͺΓ&MPRVFOUΛૉʹͬͨ΄͏͕-BSBWFM׆͖Δ w -BSBWFMͷརͷҰͭͰ͋Δ։ൃͲ͏ͯ٘͠ਜ਼ʹͳΔ লʢͬͯΈ͔ͯͬͨ͜ͱʣ
w %%%͕ઃܭख๏ͱͯ͠༗༻ͳͷ͔͕֬ͩɺͲ͜·Ͱίʔυʹམͱ͠ ࠐΉ͔બͷ༨ʢ෯ʣ͕͋Δͱࢥ͏ w Ϣʔβʔʹ͍ۙ෦ૉૣ͘։ൃɾ෮͍ͨ͠ʢ-BSBWFMΒ͘͠࡞Γ͍ͨʣ w Ϗδωε্ͷཧ༝ʹΑΓ༷ͱ6*͕ໃ६͢Δ͜ͱΑ͋͘Δ͜ͱ w %%%ͰखʹೖΔݎ࿚ੑɾอकੑ٘ਜ਼ʹͨ͘͠ͳ͍ w
4P& 4ZTUFNPG&OHBHFNFOU 4P3 4ZTUFNPG3FDPSE ͱ͍͏ΓޱͰ ΞϓϦέʔγϣϯΛׂ͢ΔͷΞϦ͔͠Εͳ͍ w 4P3తͳ෦͕ಷॏʹͳ͍͚ͬͯͳ͍ˠ.JDSPTFSWJDFT"SDIJUFDUVSF Ͳ͏͢Εྑ͍ͷͩΖ͏ʁ
4P&4P3ͱ͍͏ΓޱͰΞϓϦέʔγϣϯΛׂʁ "QQMJDBUJPO 4P& %PNBJO" 4P3 *OGSBTUSVDUVSF" %#֎෦"1* ૉૣ͍։ൃɾ෮ ݎ࿚ੑɾอकੑʢେ͖͘ͳΓ͗͢ͳ͍Α͏ʹׂʣ ಈతܕ͚ݴޠʁ
੩తܕ͚ݴޠʁ %PNBJO# 4P3
·ͱΊ
w %%%ΛऔΓೖΕΔԸܙେ͖͍ʢֶͼͱ҆৺ײʣ w ֦ுʹରͯ͠։͔Ε͍ͯΔͷ-BSBWFMͷྑ͍ͱ͜Ζ w ΞϓϦέʔγϣϯͷੑ࣭ߏɺݴޠɾ'8ͷಛੑΛؑΈͯɺऔΓೖ Εํͬͱߟ͍͖͍͑ͯͨ ·ͱΊ
ΫϥγίϜͰΤϯδχΞɾσβΠφʔΛืू͍ͯ͠·͢ IUUQTXXXXBOUFEMZDPNDPNQBOJFTLVSBTIJDPN
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠