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.7k
Hasura.io RDBをサクサク作る方法はARやO/RMだけじゃなくなりました/hasura-io
okunokentaro
5
670
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.4k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1.1k
Other Decks in Technology
See All in Technology
バクラクによるコーポレート業務の自動運転 #BetAIDay
layerx
PRO
1
540
AIエージェントを支える設計
tkikuchi1002
12
2.8k
From Live Coding to Vibe Coding with Firebase Studio
firebasethailand
1
350
ファインディにおける Dataform ブランチ戦略
hiracky16
0
240
大規模イベントに向けた ABEMA アーキテクチャの遍歴 ~ Platform Strategy 詳細解説 ~
nagapad
0
130
Claude Codeが働くAI中心の業務システム構築の挑戦―AIエージェント中心の働き方を目指して
os1ma
9
1.3k
Perlアプリケーションで トレースを実装するまでの 工夫と苦労話
masayoshi
1
320
「育てる」サーバーレス 〜チーム開発研修で学んだ、小さく始めて大きく拡張するAWS設計〜
yu_kod
1
230
東京海上日動におけるセキュアな開発プロセスの取り組み
miyabit
0
220
AI によるドキュメント処理を加速するためのOCR 結果の永続化と再利用戦略
tomoaki25
0
280
Vision Language Modelと自動運転AIの最前線_20250730
yuyamaguchi
2
980
TypeScript 上達の道
ysknsid25
23
5.1k
Featured
See All Featured
Facilitating Awesome Meetings
lara
54
6.5k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
8
860
Music & Morning Musume
bryan
46
6.7k
A better future with KSS
kneath
238
17k
How to Ace a Technical Interview
jacobian
278
23k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3.1k
Bash Introduction
62gerente
613
210k
It's Worth the Effort
3n
185
28k
Documentation Writing (for coders)
carmenintech
73
4.9k
Designing for humans not robots
tammielis
253
25k
Designing for Performance
lara
610
69k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
8
720
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