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
7
8.8k
クラシコムと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
3k
開発・CI・運用におけるDocker戦略(クラシコムの場合)/ Dockerising Apps at Kurashicom
takeru0757
1
3.5k
SimpleとEasyは違う / Simple is not Easy
takeru0757
94
44k
クラウドワークスとデザインガイドライン
takeru0757
2
1.5k
ユーザーのためのデザイン。ユーザーのためのデザイン組織。
takeru0757
4
4.6k
デザイナーとデザイナーじゃない人でデザイン組織を作る
takeru0757
4
3.5k
受託会社とサービス運営会社におけるUXの考え方の違い
takeru0757
1
2.5k
Other Decks in Programming
See All in Programming
C++でシェーダを書く
fadis
6
4.1k
イベント駆動で成長して委員会
happymana
1
340
Remix on Hono on Cloudflare Workers
yusukebe
1
300
Realtime API 入門
riofujimon
0
150
3rd party scriptでもReactを使いたい! Preact + Reactのハイブリッド開発
righttouch
PRO
1
610
What’s New in Compose Multiplatform - A Live Tour (droidcon London 2024)
zsmb
1
480
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
220
『ドメイン駆動設計をはじめよう』のモデリングアプローチ
masuda220
PRO
8
540
Micro Frontends Unmasked Opportunities, Challenges, Alternatives
manfredsteyer
PRO
0
110
flutterkaigi_2024.pdf
kyoheig3
0
150
OnlineTestConf: Test Automation Friend or Foe
maaretp
0
120
RubyLSPのマルチバイト文字対応
notfounds
0
120
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.8k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
Designing the Hi-DPI Web
ddemaree
280
34k
10 Git Anti Patterns You Should be Aware of
lemiorhan
655
59k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
What's new in Ruby 2.0
geeforr
343
31k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
720
Music & Morning Musume
bryan
46
6.2k
Product Roadmaps are Hard
iamctodd
PRO
49
11k
The World Runs on Bad Software
bkeepers
PRO
65
11k
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
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠