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
680
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.4k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1.1k
Other Decks in Technology
See All in Technology
AIのグローバルトレンド2025 #scrummikawa / global ai trend
kyonmm
PRO
1
280
Snowflake Intelligenceにはこうやって立ち向かう!クラシルが考えるAI Readyなデータ基盤と活用のためのDataOps
gappy50
0
180
KotlinConf 2025_イベントレポート
sony
1
130
20250913_JAWS_sysad_kobe
takuyay0ne
2
180
react-callを使ってダイヤログをいろんなとこで再利用しよう!
shinaps
1
240
roppongirb_20250911
igaiga
1
220
Aurora DSQLはサーバーレスアーキテクチャの常識を変えるのか
iwatatomoya
1
920
未経験者・初心者に贈る!40分でわかるAndroidアプリ開発の今と大事なポイント
operando
5
480
Generative AI Japan 第一回生成AI実践研究会「AI駆動開発の現在地──ブレイクスルーの鍵を握るのはデータ領域」
shisyu_gaku
0
160
品質視点から考える組織デザイン/Organizational Design from Quality
mii3king
0
200
機械学習を扱うプラットフォーム開発と運用事例
lycorptech_jp
PRO
0
230
Webアプリケーションにオブザーバビリティを実装するRust入門ガイド
nwiizo
7
800
Featured
See All Featured
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
51
5.6k
Producing Creativity
orderedlist
PRO
347
40k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
23
1.4k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.9k
Thoughts on Productivity
jonyablonski
70
4.8k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3.1k
Context Engineering - Making Every Token Count
addyosmani
2
41
Site-Speed That Sticks
csswizardry
10
810
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
Faster Mobile Websites
deanohume
309
31k
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