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
OKUNOKENTARO
December 01, 2019
Technology
1
3.9k
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
11k
Podcastを継続する技術 / refactoradio-240119
okunokentaro
1
190
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
690
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.4k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1.1k
Other Decks in Technology
See All in Technology
「タコピーの原罪」から学ぶ間違った”支援” / the bad support of Takopii
piyonakajima
0
160
マルチエージェントのチームビルディング_2025-10-25
shinoyamada
0
230
文字列操作の達人になる ~ Kotlinの文字列の便利な世界 ~ - Kotlin fest 2025
tomorrowkey
2
290
AI時代の発信活動 ~技術者として認知してもらうための発信法~ / 20251028 Masaki Okuda
shift_evolve
PRO
1
130
serverless team topology
_kensh
3
250
書籍『実践 Apache Iceberg』の歩き方
ishikawa_satoru
0
380
プロダクト開発と社内データ活用での、BI×AIの現在地 / Data_Findy
sansan_randd
1
710
アノテーション作業書作成のGood Practice
cierpa0905
PRO
1
340
Zero Trust DNS でより安全なインターネット アクセス
murachiakira
0
130
組織全員で向き合うAI Readyなデータ利活用
gappy50
5
1.9k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
2
190
DSPy入門
tomehirata
6
790
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
44
7.9k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.1k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
10
900
The Pragmatic Product Professional
lauravandoore
36
7k
GraphQLの誤解/rethinking-graphql
sonatard
73
11k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
51k
Faster Mobile Websites
deanohume
310
31k
A Modern Web Designer's Workflow
chriscoyier
697
190k
A better future with KSS
kneath
239
18k
Rails Girls Zürich Keynote
gr2m
95
14k
How to train your dragon (web standard)
notwaldorf
97
6.3k
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