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
96
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
AI 時代だからこそ抑えたい「価値のある」PHP ユニットテストを書く技術 #phpconfuk / phpcon-fukuoka-2025
shogogg
1
400
SidekiqでAIに商品説明を生成させてみた
akinko_0915
0
130
What’s Fair is FAIR: A Decentralised Future for WordPress Distribution
rmccue
0
150
CloudflareのSandbox SDKを試してみた
syumai
0
120
開発組織の戦略的な役割と 設計スキル向上の効果
masuda220
PRO
10
2.2k
仕様がそのままテストになる!Javaで始める振る舞い駆動開発
ohmori_yusuke
1
630
퇴근 후 1억이 거래되는 서비스 만들기 | 내가 AI를 사용하는 방법
maryang
2
540
Flutterアプリ運用の現場で役立った監視Tips 5選
ostk0069
1
290
Researchlyの開発で参考にしたデザイン
adsholoko
0
120
What's New in Web AI?
christianliebel
PRO
0
120
ネストしたdata classの面倒な更新にさようなら!Lensを作って理解するArrowのOpticsの世界
shiita0903
1
290
なんでRustの環境構築してないのにRust製のツールが動くの? / Why Do Rust-Based Tools Run Without a Rust Environment?
ssssota
15
48k
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
660
Automating Front-end Workflow
addyosmani
1371
200k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
127
54k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
14k
GraphQLとの向き合い方2022年版
quramy
49
14k
Agile that works and the tools we love
rasmusluckow
331
21k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.8k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Building Applications with DynamoDB
mza
96
6.7k
The World Runs on Bad Software
bkeepers
PRO
72
12k
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
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠