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
Building the layered server application achieve...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
OKUNOKENTARO
December 01, 2019
Technology
1
4.1k
Building the layered server application achieved by InversifyJS
The slide that used when I talked in JSConf JP.
OKUNOKENTARO
December 01, 2019
Tweet
Share
More Decks by OKUNOKENTARO
See All by OKUNOKENTARO
トレタO/X アーキテクチャ移行記 Next.js App Router化への道のり / TORETA TECH UPDATE 1
okunokentaro
5
12k
Podcastを継続する技術 / refactoradio-240119
okunokentaro
1
200
Webアプリケーション設計の第一歩は ディレクトリの整理から / Encraft 1
okunokentaro
34
10k
JSONとJSON Schemaを改めて理解する / tokyo_study
okunokentaro
9
2.4k
それでもどうしてRecoilを使うのか / Harajuku.ts Meetup Recoil
okunokentaro
19
5.6k
TypeScriptは10年でこんなに進化しました / TechFeed Experts Night 11
okunokentaro
6
1.8k
Hasura.io RDBをサクサク作る方法はARやO/RMだけじゃなくなりました/hasura-io
okunokentaro
5
700
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.5k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1.1k
Other Decks in Technology
See All in Technology
Oracle AI Database移行・アップグレード勉強会 - RAT活用編
oracle4engineer
PRO
0
110
Ruby版 JSXのRuxが気になる
sansantech
PRO
0
170
SchooでVue.js/Nuxtを技術選定している理由
yamanoku
3
200
10Xにおける品質保証活動の全体像と改善 #no_more_wait_for_test
nihonbuson
PRO
2
330
プロポーザルに込める段取り八分
shoheimitani
1
630
GitHub Issue Templates + Coding Agentで簡単みんなでIaC/Easy IaC for Everyone with GitHub Issue Templates + Coding Agent
aeonpeople
1
260
AWS Network Firewall Proxyを触ってみた
nagisa53
1
240
SREのプラクティスを用いた3領域同時 マネジメントへの挑戦 〜SRE・情シス・セキュリティを統合した チーム運営術〜
coconala_engineer
2
770
AIが実装する時代、人間は仕様と検証を設計する
gotalab555
1
190
生成AIと余白 〜開発スピードが向上した今、何に向き合う?〜
kakehashi
PRO
0
140
22nd ACRi Webinar - NTT Kawahara-san's slide
nao_sumikawa
0
100
ECS障害を例に学ぶ、インシデント対応に備えたAIエージェントの育て方 / How to develop AI agents for incident response with ECS outage
iselegant
3
290
Featured
See All Featured
Context Engineering - Making Every Token Count
addyosmani
9
670
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
280
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Design in an AI World
tapps
0
150
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
The World Runs on Bad Software
bkeepers
PRO
72
12k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.7k
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
ラッコキーワード サービス紹介資料
rakko
1
2.3M
Unsuck your backbone
ammeep
671
58k
Transcript
#VJMEJOHUIFMBZFSFETFSWFSBQQMJDBUJPO BDIJFWFECZ*OWFSTJGZ+4 %FD +4$POG+1 !PLVOPLFOUBSP
+4$POG+1 ˙ ,FOԞݡଠ !PLVOPLFOUBSP ˙ $MJFOUTJEF4FSWFSTJEFEFWFMPQFS ˙ "OHVMBS+BQBO6TFS(SPVQ
+4$POG+1 "HFOEB ˙ .ZBQQMJDBUJPOBSDIJUFDUVSF ˙ 8IBUJTUJHIUDPVQMJOH 8IZEPXFOFFE%*1 ˙ %FWFMPQJOHXJUI
5ZQF4DSJQU &YQSFTT *OWFSTJGZ+4 ,OFYKT
8IBUUPEFWFMPQ
+4$POG+1 8IBUUPEFWFMPQ ˙ 5IFOFX4BB4UIBUDIBOHFTUIFXBZZPVMJTUFOUP NVTJD
+4$POG+1 )VNCMFQSPCMFNT ˙ *UTIPVMEIBWFCFFOSFMFBTFEOPX XIFO*BQQMJFEGPS$'1 ˙ %FWFMPQNFOUQFOEJOH ˙ 8JMMUBMLBCPVUEFWFMPQJOHBUFTUWFSTJPO
4ZTUFNBSDIJUFDUVSF
+4$POG+1 ˙ (PPHMF$MPVE1MBUGPSN ˙ $MPVE42- ˙ "QQ&OHJOF 4UBOEBSEFOWJSPONFOU XJUI/PEFKT
˙ $MPVE4UPSBHF
"QQMJDBUJPOBSDIJUFDUVSF
+4$POG+1 ˙ 5ZQF4DSJQU ˙ 4FSWFS&YQSFTT ˙ $MJFOU"OHVMBS
8IBUJTUJHIUDPVQMJOH
+4$POG+1 5JHIUDPVQMJOH ˙ %JGpDVMUTJUVBUJPOUPEFUFSNJOFXIJDIDPEFDIBOHFT XJMMBGGFDUXIFSF ˙ *OUSJDBUFMZJOUFSUXJOFE ˙ 4DIFNB FYUFSOBMTFSWJDFT
CVTJOFTTSVMFT TUSVDUVSFPG3&45"1* (6*FMFNFOUT
'PSFYBNQMF
+4$POG+1 ˙ $SFBUFEB3&45"1*XJUIBEBUBTUSVDUVSFTJNJMBSUP UFYUpFMETPOUIFTDSFFO ˙ 1FSTJTUFEUIF+40/BTJUJTJOUIFEBUBCBTF
+4$POG+1 ˙ 0VUCSFBLPGSFEFTJHO ˙ 5FYUpFMETXFSFTQMJUJOUPNVMUJQMFTFDUJPOT XIFOUIFDMJFOUBQQXBTSFEFTJHOFE ˙ "EEFETDSFFOUSBOTJUJPO
+4$POG+1 ˙ %BUBCBTFTDIFNBXBTTUSPOHMZEFQFOEFOU POUIFTDSFFOCFGPSFSFEFTJHO ˙ $POTJTUFODZIBTCFFOMPTU
+4$POG+1 8IZ Screen before redesigned Schema before redesigned Redesigned screen
Schema before redesigned
+4$POG+1 8IBUTIPVMEXFEP Redesigned screen Schema unaffected by a screen design
Abstract interface
+4$POG+1 -BZFSJOH
-BZFSFEBSDIJUFDUVSF
+4$POG+1 4PMJUBSZCBUUMF ˙ 8IFOTFSWFSBOEDMJFOUBSFJNQMFNFOUFEBMPOF XFDPVMEXSJUFDPEFTXJUIQFSGFDUGSFFEPN ˙ 5IBUJT
%JTUVSCFEBSFB
PS
+4$POG+1 Render and handle event Persist
+4$POG+1 Render and handle event Persist Client Server
+4$POG+1 Render and handle event Persist Usecases Abstract storages Mediator
Repository HTTP
+4$POG+1 Render and handle event Persist Usecases Abstract storages Mediator
Repository HTTP -BZFSFEDMJFOU -BZFSFETFSWFS
+4$POG+1 #SJOHPSEFS ˙ 3FHBSEMFTTPGUIFOVNCFSPGEFWFMPQFST BMXBZTCFBXBSFPGEJTDJQMJOFEDPEFEJWJTJPOT ˙ -PPLTMJLFBOPSHBOJ[FETIFMG ˙ "CMFUPEJWJEFMBCPS
4PNFEBZ
+4$POG+1 ˙ %%%%PNBJO%SJWFO%FTJHO ˙ 5PVHIUPUBLF%%%QFSGFDUMZ ˙ ,OPXUIFDPODFQUBOECPSSPXJU ˙ .%%.PEFM%SJWFO%FWFMPQNFOU ˙
3FQPTJUPSZ%BUBQFSTJTUFODFBCTUSBDUJPO
3FQPTJUPSZQBUUFSO
+4$POG+1 ˙ %BUBCBTFJTBMNPTUBMXBZTOPSNBMJ[FE ˙ .VMUJQMFUBCMFT ˙ .VMUJQMFSFBETBOEXSJUFT ˙ .BOZ+0*/ ˙
.BOZUSBOTBDUJPOT
+4$POG+1 $IBPTEVSJOHNJHSBUJPO ˙ *UTQBJOGVMUPpYUIFXIPMFBQQMJDBUJPO XJUIFBDI%#NJHSBUJPOT ˙ 7JPMBUFT0JO40-*%QSJODJQMFT ˙ 0$1&OUJUJFTTIPVMECFPQFOGPSFYUFOTJPO
CVUDMPTFEGPSNPEJpDBUJPO
+4$POG+1 3FQPTJUPSZ ˙ %BUBCBTFBCTUSBDUJPO ˙ %FDPVQMFTEBUBCBTFNBQQJOHGSPN UIFEPNBJONPEFMJUTFMG ˙ 4USFOHUIFOTQFDJpDBUJPODIBOHFT
+4$POG+1 %#DPOOFDUJPO ˙ .BOZDSFEFOUJBMTBSFUSFBUFEBTFOWJSPONFOU WBSJBCMFT ˙ %BUBCBTFOBNFT VTFST QBTTXPSET ˙
.PTUPGUIFNNBZCFTQFDJpFEJOEJWJEVBMMZ GPSQSPEVDUBOEEFWFMPQNFOU
+4$POG+1 %JGpDVMUUPUFTU ˙ *GZPVDPOOFDUUP%#GPSQSPEVDUJPO FWFSZUJNFZPVUFTU ZPVDBOUUFTUPGqJOF ˙ 8IFOQSFQBSJOHBpYUVSF%#GPSUFTUJOH ZPVDBOUEPJUFWFOJGZPVXBOUUPTQMJUJU
)PX
%FQFOEFODZ*OWFSTJPO1SJODJQMF
+4$POG+1 ˙ %*1 ˙ %JO40-*%QSJODJQMFT
+4$POG+1 ˙ )JHIMFWFMNPEVMFTTIPVMEOPUEFQFOEPOMPXMFWFM NPEVMFT ˙ #PUITIPVMEEFQFOEPOBCTUSBDUJPOT ˙ "CTUSBDUJPOTTIPVMEOPUEFQFOEPOEFUBJMT ˙ %FUBJMTTIPVMEEFQFOEPOBCTUSBDUJPOT
3JHIU
.PSFGBNJMJBSFYBNQMFT☝
class UserId { readonly value: string; constructor() { this.value =
uuid(); } } /PUHPPE
function makeUserId(): UserId { return new UserId(uuid()); } function makeUserIdMock(mockValue:
string): UserId { return new UserId(mockValue); } class UserId { constructor(readonly value: string) {} } #FUUFS
class UsersRepository { private readonly db: Knex; constructor() { this.db
= knex(config); } } /PUHPPE
class UsersRepository { constructor( private readonly db: DatabaseProvider ) {}
} #FUUFS
class UsersGetMediator { async handle(req: Request, res: Response) { const
repo = new UsersRepository( new DatabaseProvider(), ); const users = await repo.getAll(); // Processing to send response } } )NNN
class Application { private router: Router; constructor() { this.router =
new Router( new UsersGetMediator( new UsersRepository( new DatabaseProvider(), ) ) ) } } :PVLOPX
class Application { private router: Router; constructor() { this.router =
new Router( new UsersGetMediator( new UsersRepository( new DatabaseProvider(), ) ) ) } } )!%0,&/
*TUIJTPLBZ
-FUTVTF%*DPOUBJOFS
import { injectable } from 'inversify'; @injectable() export class Application
{ constructor( private readonly auth: AuthService, private readonly router: Router, private readonly express: ExpressProvider, ) {} main() { this.auth.prepare(); this.router.prepare(); this.express.listen(); } } *OKFDUBCMFEFDPSBUPS
@injectable() export class UsersGetMediator { constructor( private readonly auth: AuthService,
private readonly permissionVerifier: PermissionVerifier, private readonly errorHandler: ErrorHandler, private readonly usersRepo: UsersRepository, ) {} async handle(req: Request, res: Response) { if (!(await this.auth.isAuthenticated(req))) { return this.errorHandler.unauthorized(res); } if (!(await this.permissionVerifier.canReadUsers(req.user.id))) { return this.errorHandler.forbidden(res); } const users = await this.usersRepo.getAll(); const body: UsersGetResponse = { users: users.serialize(), }; res.send(body).end(); } }
+4$POG+1 *OWFSTJGZ+4 ˙ "CMFUPIBOEMF"constructor injection" ˙ *OUFSGBDFTJNJMBSUP"OHVMBS ˙ IUUQTHJUIVCDPNJOWFSTJGZ*OWFSTJGZ+4
I was going to introduce the trick here...
But I have found it...
+4$POG+1 54ZSJOHF ˙ .BEFCZ.JDSPTPGU ˙ *OUFSGBDFTJNJMBSUP*OWFSTJGZ+4 *OPUIFSXPSET TJNJMBSUP"OHVMBS ˙ /POFFEGPSNZUSJDLTGPS*OWFSTJGZ+4
˙ IUUQTHJUIVCDPNNJDSPTPGUUTZSJOHF
%FWFMPQJOHXJUI 5ZQF4DSJQU &YQSFTT *OWFSTJGZ+4 ,OFYKT
+4$POG+1 Express Http Handler (Router) Google Cloud SQL Mediator Repository
Database Provider Reader Writer
+4$POG+1 Express Http Handler (Router) Google Cloud SQL Mediator Repository
Database Provider Reader Writer Http Request (Not TS) Domain model Data access object Knex.js ORM Raw SQL Query (Not TS)
+4$POG+1 Express Http Handler (Router) Google Cloud SQL Mediator Repository
Database Provider Reader Writer Serialized JSON (Not TS) Domain model Data transfer object Results as JSON (Not TS)
+4$POG+1 ˙ *OUSPEVDF"value object"CZ%%% ˙ "WPJEVTJOHQSJNJUJWFUZQFTBTNVDIBTQPTTJCMF MJLFstring number ˙ 'PSFYBNQMF
UserId UserName AccessToken Email
+4$POG+1 ˙ /PUVTFBWBMJEBUPSGVODUJPOBOEBOifTUBUFNFOU ˙ 7FSJGZCZUISPXJOHBOFYDFQUJPOCZUIFDPOTUSVDUPS ˙ *GJOTUBOUJBUJPOJTTVDDFTTGVM BMXBZTWBMJEBUFEBOEDPNQJMFTBGF
+4$POG+1 8IZEPOU*VTF/FTU+4 ˙ IUUQTOFTUKTDPN ˙ /FTU+4IBTEFQFOEFODZJOKFDUJPO ˙ *QSFGFS"OHVMBSCVU*MJLFUPDPNCJOF UIFNJOJNVNSFRVJSFEGFBUVSFTPOUIFTFSWFSTJEF ˙
5BTUFTEJGGFS
-FUTTUBZPSHBOJ[FE XJUI%*$POUBJOFS
5IBOLZPV