Building the layered server application achieved by InversifyJS

2bedb1eb8f841cd3c3ae584600b016e0?s=47 OKUNOKENTARO
December 01, 2019

Building the layered server application achieved by InversifyJS

The slide that used when I talked in JSConf JP.

2bedb1eb8f841cd3c3ae584600b016e0?s=128

OKUNOKENTARO

December 01, 2019
Tweet

Transcript

  1. #VJMEJOHUIFMBZFSFETFSWFSBQQMJDBUJPO
 BDIJFWFECZ*OWFSTJGZ+4 %FD +4$POG+1 !PLVOPLFOUBSP

  2. +4$POG+1 ˙ ,FOԞ໺ݡଠ࿠
 !PLVOPLFOUBSP ˙ $MJFOUTJEF4FSWFSTJEFEFWFMPQFS ˙ "OHVMBS+BQBO6TFS(SPVQ

  3. +4$POG+1 "HFOEB ˙ .ZBQQMJDBUJPOBSDIJUFDUVSF ˙ 8IBUJTUJHIUDPVQMJOH 8IZEPXFOFFE%*1  ˙ %FWFMPQJOHXJUI


    5ZQF4DSJQU &YQSFTT *OWFSTJGZ+4 ,OFYKT
  4. 8IBUUPEFWFMPQ

  5. +4$POG+1 8IBUUPEFWFMPQ ˙ 5IFOFX4BB4UIBUDIBOHFTUIFXBZZPVMJTUFOUP NVTJD

  6. +4$POG+1 )VNCMFQSPCMFNT ˙ *UTIPVMEIBWFCFFOSFMFBTFEOPX
 XIFO*BQQMJFEGPS$'1 ˙ %FWFMPQNFOUQFOEJOH ˙ 8JMMUBMLBCPVUEFWFMPQJOHBUFTUWFSTJPO

  7. 4ZTUFNBSDIJUFDUVSF

  8. +4$POG+1 ˙ (PPHMF$MPVE1MBUGPSN ˙ $MPVE42- ˙ "QQ&OHJOF 4UBOEBSEFOWJSPONFOU 
 XJUI/PEFKT

    ˙ $MPVE4UPSBHF
  9. "QQMJDBUJPOBSDIJUFDUVSF

  10. +4$POG+1 ˙ 5ZQF4DSJQU ˙ 4FSWFS&YQSFTT ˙ $MJFOU"OHVMBS

  11. 8IBUJTUJHIUDPVQMJOH

  12. +4$POG+1 5JHIUDPVQMJOH ˙ %JGpDVMUTJUVBUJPOUPEFUFSNJOFXIJDIDPEFDIBOHFT XJMMBGGFDUXIFSF ˙ *OUSJDBUFMZJOUFSUXJOFE ˙ 4DIFNB FYUFSOBMTFSWJDFT

    CVTJOFTTSVMFT 
 TUSVDUVSFPG3&45"1* (6*FMFNFOUT
  13. 'PSFYBNQMF

  14. +4$POG+1 ˙ $SFBUFEB3&45"1*XJUIBEBUBTUSVDUVSFTJNJMBSUP
 UFYUpFMETPOUIFTDSFFO ˙ 1FSTJTUFEUIF+40/BTJUJTJOUIFEBUBCBTF

  15. +4$POG+1 ˙ 0VUCSFBLPGSFEFTJHO ˙ 5FYUpFMETXFSFTQMJUJOUPNVMUJQMFTFDUJPOT
 XIFOUIFDMJFOUBQQXBTSFEFTJHOFE ˙ "EEFETDSFFOUSBOTJUJPO

  16. +4$POG+1 ˙ %BUBCBTFTDIFNBXBTTUSPOHMZEFQFOEFOU
 POUIFTDSFFOCFGPSFSFEFTJHO ˙ $POTJTUFODZIBTCFFOMPTU

  17. +4$POG+1 8IZ Screen before redesigned Schema before redesigned Redesigned screen

    Schema before redesigned
  18. +4$POG+1 8IBUTIPVMEXFEP Redesigned screen Schema unaffected by a screen design

    Abstract interface
  19. +4$POG+1 -BZFSJOH

  20. -BZFSFEBSDIJUFDUVSF

  21. +4$POG+1 4PMJUBSZCBUUMF ˙ 8IFOTFSWFSBOEDMJFOUBSFJNQMFNFOUFEBMPOF 
 XFDPVMEXSJUFDPEFTXJUIQFSGFDUGSFFEPN ˙ 5IBUJT

  22. %JTUVSCFEBSFB

  23. PS

  24. +4$POG+1 Render and
 handle event Persist

  25. +4$POG+1 Render and
 handle event Persist Client Server

  26. +4$POG+1 Render and
 handle event Persist Usecases Abstract storages Mediator

    Repository HTTP
  27. +4$POG+1 Render and
 handle event Persist Usecases Abstract storages Mediator

    Repository HTTP -BZFSFEDMJFOU -BZFSFETFSWFS
  28. +4$POG+1 #SJOHPSEFS ˙ 3FHBSEMFTTPGUIFOVNCFSPGEFWFMPQFST 
 BMXBZTCFBXBSFPGEJTDJQMJOFEDPEFEJWJTJPOT ˙ -PPLTMJLFBOPSHBOJ[FETIFMG ˙ "CMFUPEJWJEFMBCPS

    4PNFEBZ
  29. +4$POG+1 ˙ %%%%PNBJO%SJWFO%FTJHO ˙ 5PVHIUPUBLF%%%QFSGFDUMZ ˙ ,OPXUIFDPODFQUBOECPSSPXJU ˙ .%%.PEFM%SJWFO%FWFMPQNFOU ˙

    3FQPTJUPSZ%BUBQFSTJTUFODFBCTUSBDUJPO
  30. 3FQPTJUPSZQBUUFSO

  31. +4$POG+1 ˙ %BUBCBTFJTBMNPTUBMXBZTOPSNBMJ[FE ˙ .VMUJQMFUBCMFT ˙ .VMUJQMFSFBETBOEXSJUFT ˙ .BOZ+0*/ ˙

    .BOZUSBOTBDUJPOT
  32. +4$POG+1 $IBPTEVSJOHNJHSBUJPO ˙ *UTQBJOGVMUPpYUIFXIPMFBQQMJDBUJPO
 XJUIFBDI%#NJHSBUJPOT ˙ 7JPMBUFT0JO40-*%QSJODJQMFT ˙ 0$1&OUJUJFTTIPVMECFPQFOGPSFYUFOTJPO 


    CVUDMPTFEGPSNPEJpDBUJPO
  33. +4$POG+1 3FQPTJUPSZ ˙ %BUBCBTFBCTUSBDUJPO ˙ %FDPVQMFTEBUBCBTFNBQQJOHGSPN
 UIFEPNBJONPEFMJUTFMG ˙ 4USFOHUIFOTQFDJpDBUJPODIBOHFT

  34. +4$POG+1 %#DPOOFDUJPO ˙ .BOZDSFEFOUJBMTBSFUSFBUFEBTFOWJSPONFOU WBSJBCMFT ˙ %BUBCBTFOBNFT VTFST QBTTXPSET ˙

    .PTUPGUIFNNBZCFTQFDJpFEJOEJWJEVBMMZ
 GPSQSPEVDUBOEEFWFMPQNFOU
  35. +4$POG+1 %JGpDVMUUPUFTU ˙ *GZPVDPOOFDUUP%#GPSQSPEVDUJPO
 FWFSZUJNFZPVUFTU ZPVDBOUUFTUPGqJOF ˙ 8IFOQSFQBSJOHBpYUVSF%#GPSUFTUJOH 
 ZPVDBOUEPJUFWFOJGZPVXBOUUPTQMJUJU

  36. )PX 

  37. %FQFOEFODZ*OWFSTJPO1SJODJQMF

  38. +4$POG+1 ˙ %*1 ˙ %JO40-*%QSJODJQMFT

  39. +4$POG+1 ˙ )JHIMFWFMNPEVMFTTIPVMEOPUEFQFOEPOMPXMFWFM NPEVMFT ˙ #PUITIPVMEEFQFOEPOBCTUSBDUJPOT ˙ "CTUSBDUJPOTTIPVMEOPUEFQFOEPOEFUBJMT ˙ %FUBJMTTIPVMEEFQFOEPOBCTUSBDUJPOT

  40. 3JHIU

  41. .PSFGBNJMJBSFYBNQMFT☝

  42. class UserId { readonly value: string; constructor() { this.value =

    uuid(); } } /PUHPPE
  43. function makeUserId(): UserId { return new UserId(uuid()); } function makeUserIdMock(mockValue:

    string): UserId { return new UserId(mockValue); } class UserId { constructor(readonly value: string) {} } #FUUFS
  44. class UsersRepository { private readonly db: Knex; constructor() { this.db

    = knex(config); } } /PUHPPE
  45. class UsersRepository { constructor( private readonly db: DatabaseProvider ) {}

    } #FUUFS
  46. class UsersGetMediator { async handle(req: Request, res: Response) { const

    repo = new UsersRepository( new DatabaseProvider(), ); const users = await repo.getAll(); // Processing to send response } } )NNN
  47. class Application { private router: Router; constructor() { this.router =

    new Router( new UsersGetMediator( new UsersRepository( new DatabaseProvider(), ) ) ) } } :PVLOPX
  48. class Application { private router: Router; constructor() { this.router =

    new Router( new UsersGetMediator( new UsersRepository( new DatabaseProvider(), ) ) ) } } )!%0,&/
  49. *TUIJTPLBZ

  50. -FUTVTF%*DPOUBJOFS

  51. 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
  52. @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(); } }
  53. +4$POG+1 *OWFSTJGZ+4 ˙ "CMFUPIBOEMF"constructor injection" ˙ *OUFSGBDFTJNJMBSUP"OHVMBS ˙ IUUQTHJUIVCDPNJOWFSTJGZ*OWFSTJGZ+4

  54. I was going to introduce the trick here...

  55. But I have found it...

  56. +4$POG+1 54ZSJOHF ˙ .BEFCZ.JDSPTPGU ˙ *OUFSGBDFTJNJMBSUP*OWFSTJGZ+4
 *OPUIFSXPSET TJNJMBSUP"OHVMBS ˙ /POFFEGPSNZUSJDLTGPS*OWFSTJGZ+4

    ˙ IUUQTHJUIVCDPNNJDSPTPGUUTZSJOHF
  57. %FWFMPQJOHXJUI
 5ZQF4DSJQU &YQSFTT  *OWFSTJGZ+4 ,OFYKT

  58. +4$POG+1 Express Http Handler (Router) Google Cloud SQL Mediator Repository

    Database Provider Reader Writer
  59. +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)
  60. +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)
  61. +4$POG+1 ˙ *OUSPEVDF"value object"CZ%%% ˙ "WPJEVTJOHQSJNJUJWFUZQFTBTNVDIBTQPTTJCMF
 MJLFstring number ˙ 'PSFYBNQMF


    UserId UserName AccessToken Email
  62. +4$POG+1 ˙ /PUVTFBWBMJEBUPSGVODUJPOBOEBOifTUBUFNFOU ˙ 7FSJGZCZUISPXJOHBOFYDFQUJPOCZUIFDPOTUSVDUPS ˙ *GJOTUBOUJBUJPOJTTVDDFTTGVM 
 BMXBZTWBMJEBUFEBOEDPNQJMFTBGF

  63. +4$POG+1 8IZEPOU*VTF/FTU+4 ˙ IUUQTOFTUKTDPN ˙ /FTU+4IBTEFQFOEFODZJOKFDUJPO ˙ *QSFGFS"OHVMBSCVU*MJLFUPDPNCJOF
 UIFNJOJNVNSFRVJSFEGFBUVSFTPOUIFTFSWFSTJEF ˙

    5BTUFTEJGGFS
  64. -FUTTUBZPSHBOJ[FE XJUI%*$POUBJOFS

  65. 5IBOLZPV