https://github.com/falsy/clean-architecture-for-frontend
A sample project showcasing Clean Architecture and monorepo structure for designing multiple web services with a shared domain.
https://github.com/falsy/clean-architecture-for-frontend
clean-architecture ddd documentation oop typescript web
Last synced: about 1 year ago
JSON representation
A sample project showcasing Clean Architecture and monorepo structure for designing multiple web services with a shared domain.
- Host: GitHub
- URL: https://github.com/falsy/clean-architecture-for-frontend
- Owner: falsy
- License: unlicense
- Created: 2019-12-08T11:23:59.000Z (over 6 years ago)
- Default Branch: main
- Last Pushed: 2025-05-02T15:19:54.000Z (about 1 year ago)
- Last Synced: 2025-05-10T09:37:35.017Z (about 1 year ago)
- Topics: clean-architecture, ddd, documentation, oop, typescript, web
- Language: TypeScript
- Homepage:
- Size: 34.5 MB
- Stars: 743
- Watchers: 13
- Forks: 122
- Open Issues: 0
-
Metadata Files:
- Readme: README-ko.md
- License: LICENSE
Awesome Lists containing this project
- awesome-github-projects - clean-architecture-for-frontend - A sample project showcasing Clean Architecture and monorepo structure for designing multiple web services with a shared domain. β780 `TypeScript` (π¦ Legacy & Inactive Projects)
README
# Clean Architecture for Frontend
ν΄λ¦° μν€ν
μ²λ `DDD(Domain-driven Design)`μ `MSA(Micro Service Architecture)`μ ν¨κ» λ§μ νλ‘μ νΈμμ νμ©λκ³ μμ΅λλ€. μ΄ νλ‘μ νΈμμλ TypeScriptλ₯Ό μ¬μ©νλ©΄μ λμΌν λλ©μΈμ 곡μ νλ λ€μν μΉ ν΄λΌμ΄μΈνΈ μλΉμ€λ₯Ό λͺ¨λ
Έλ ν¬ κ΅¬μ±κ³Ό ν΄λ¦° μν€ν
μ² μ€κ³λ₯Ό ν΅ν΄μ μλΉμ€λ₯Ό ν¨κ³Όμ μΌλ‘ νμ₯νκ±°λ μ μ§ λ³΄μλ₯Ό μ©μ΄νκ² νλ νλμ μμ΄λμ΄ νλ‘μ νΈμ
λλ€.
λ§μ½, νλ‘μ νΈκ° λ¨μν UIλ₯Ό λ€λ£¨λ μμ κ·λͺ¨μ νλ‘μ νΈμ΄κ±°λ API μλ²κ° ν΄λΌμ΄μΈνΈμ λ§μΆ€μΌλ‘ λμλλ νκ²½μ΄λΌλ©΄ ν΄λ¦° μν€ν
μ² λμ
μ μ€νλ € 보μΌλ¬ νλ μ΄νΈ μ½λλ‘ μΈν μ½λλ μ¦κ°μ 볡μ‘μ± μ¦κ°λ‘ μλΉμ€μ μ μ§ λ³΄μμ±μ΄ λλΉ μ§ μ μμ΅λλ€.
μν νλ‘μ νΈλ Yarnμμ κΈ°λ³ΈμΌλ‘ μ 곡νλ `Workspace`λ₯Ό μ¬μ©νμ¬ λͺ¨λ
Έλ ν¬λ₯Ό ꡬμ±νκ³ , ν¨ν€μ§λ‘ ν΄λ¦° μν€ν
μ²μ Domains λ μ΄μ΄μ Adapters λ μ΄μ΄λ₯Ό ꡬμ±νμκ³ κ°κ°μ μλΉμ€ μμ ν¨ν€μ§λ‘ ꡬμ±νλ©° κ° μλΉμ€λ Domains λ μ΄μ΄μ Adapters λ μ΄μ΄μ μμλ₯Ό κ·Έλλ‘ μ¬μ©νκ±°λ λλ μμ, νμ₯νμ¬ μλΉμ€λ₯Ό ꡬμ±ν©λλ€.
## Languages
- [English](https://github.com/falsy/clean-architecture-with-typescript)
- [νκΈ](https://github.com/falsy/clean-architecture-with-typescript/blob/main/README-ko.md)
# Clean Architecture


λ€μν μν€ν
μ²λ€μ΄ κ·Έλ¬νλ― ν΄λ¦° μν€ν
μ²κ° κ°λ κΈ°λ³Έ λͺ©μ μ κ΄μ¬μ¬λ₯Ό λΆλ¦¬νλ κ²μ
λλ€. κ°μ κ΄μ¬μ¬μ λ°λΌ κ³μΈ΅μ λλκ³ μΈλΆ ꡬνμ΄ μλ λλ©μΈ μ€μ¬μΌλ‘ μ€κ³νλ©°, λ΄λΆ μμμ΄ νλ μμν¬λ λ°μ΄ν°λ² μ΄μ€, UI λ±μ μΈλΆ μμμ μμ‘΄νμ§ μλλ‘ ν©λλ€.
- μΈλΆ ꡬν μμκ³Ό λλ©μΈ μμμ ꡬλΆν©λλ€.
- μν€ν
μ²λ νλ μμν¬μ μμ‘΄νμ§ μμ΅λλ€.
- μΈλΆ μμμ λ΄λΆ μμμ μμ‘΄ν μ μμ§λ§, λ΄λΆ μμμ μΈλΆ μμμ μμ‘΄ν μ μμ΅λλ€.
- κ³ μμ€, μ μμ€ λͺ¨λ λͺ¨λ μΆμνμ μμ‘΄ν©λλ€.
## Communitaction Flow


ν΄λ¦° μν€ν
μ²μ νλ¦μ κ°λ¨νκ² λ€μ΄μ΄κ·Έλ¨μΌλ‘ νννλ©΄ μμ κ°μ΅λλ€.
# Monorepo


λͺ¨λ
Έλ ν¬λ Domains λ μ΄μ΄μ Adapters λ μ΄μ΄ κ·Έλ¦¬κ³ μλΉμ€ λ μ΄μ΄λ₯Ό κ°κ° ν¨ν€μ§λ‘ μμ‘΄μ±μ λͺ
ννκ² κ΅¬λΆνμμ΅λλ€.
κ·Έλ¦¬κ³ λ£¨νΈμμλ TypeScript, ESLint, Jestμ κΈ°λ³Έ μ€μ μΌλ‘ νμ ν¨ν€μ§μμλ νμ₯νμ¬ μ¬μ©ν μ μμ΅λλ€.
> λ§μ½, λλ©μΈμ 곡μ νλ μ¬λ¬ μλΉμ€κ° μλ λ¨μΌ μλΉμ€λ₯Ό μν ꡬμ±μ΄λΌλ©΄ λͺ¨λ
Έλ ν¬λ₯Ό μ¬μ©νμ§ μκ³ Domainsμ Adaptersλ μ΄μ΄λ₯Ό κ°κ° ν¨ν€μ§μμ λλ ν 리λ‘, μλΉμ€ ν¨ν€μ§λ Frameworks λλ ν λ¦¬λ‘ κ΅¬μ±νμ¬, μ 체 νλ‘μ νΈλ ν¬κ² Domians, Adapters, Frameworksλ‘ λλκ³ μ΄λ₯Ό μ€μ¬μΌλ‘ μ€κ³ν μ μμ΅λλ€.
# Directory Structure
```
/packages
ββ domains
β ββ src
β ββ aggregates
β ββ entities
β ββ useCases
β ββ vos
β ββ repositories
β β ββ interface
β ββ dtos
β ββ interface
ββ adapters
β ββ src
β ββ presenters
β ββ repositories
β ββ dtos
β ββ infrastructures
β ββ interface
ββ client-a(built with React)
β ββ src
β ββ di
β ββ ...
ββ client-b(built with Next.js)
ββ src
ββ di
ββ ...
```
## Tree Shaking
μ μν νλ‘μ νΈμμλ, κ°κ°μ μλΉμ€ ν¨ν€μ§μμ κ³΅μ© ν¨ν€μ§(`Domains`, `Adapters`, `κ·Έ λ°μ μΆκ°λ μ μλ ν¨ν€μ§λ€..`)λ₯Ό μ¬μ©ν λ λΉλλ κ²°κ³Όλ¬Όμ μ°Έμ‘°νμ§ μκ³ `Source-to-Source` λ°©μμΌλ‘ μ¬μ©νκ³ μμ΅λλ€. μ΄λ μ΅μ’
μ μΌλ‘ μλΉμ€μ λͺ¨λ λ²λ€λ¬κ° ν¨κ³Όμ μΌλ‘ μ¬μ©λμ§ μλ μ½λλ₯Ό μ κ±°νκ³ μ¬μ©ν μ μμ΄μΌ νκΈ° λλ¬Έμ, κ³΅μ© ν¨ν€μ§λ λͺ¨λ `ES Modules`λ‘ μμ±ν΄μΌ ν©λλ€.
> λλΆλΆμ λͺ¨λ λ²λ€λ¬λ ES Modulesλ‘ μμ±λ μ½λμ λν΄μ νΈλ¦¬μ
°μ΄νΉμ κΈ°λ³ΈμΌλ‘ μ§μν©λλ€.
# Domains
λλ©μΈ λ μ΄μ΄μμλ λΉμ¦λμ€ κ·μΉκ³Ό λΉμ¦λμ€ λ‘μ§μ μ μν©λλ€.
μν νλ‘μ νΈμ κ²½μ°μλ κ°λ¨ν ν¬λΌ μλΉμ€μ μΌλΆλΆμΌλ‘ μ¬μ©μκ° κΈ λͺ©λ‘μ 보거λ κΈκ³Ό λκΈμ μμ±ν μ μλ μλΉμ€μ
λλ€. λͺ¨λ
Έλ ν¬λ‘ ꡬμ±λ νλμ ν¨ν€μ§λ‘ Entityμ Use Case κ·Έλ¦¬κ³ Value Object λ±μ μ μνκ³ , λ€μν μλΉμ€ ν¨ν€μ§λ μ΄λ₯Ό μ¬μ©νμ¬ μλΉμ€λ₯Ό ꡬμ±ν©λλ€.
## Entities
Entityλ λλ©μΈ λͺ¨λΈλ§μ ν΅μ¬ κ°λ
μ€ νλλ‘, κ³ μ ν μλ³μ(Identity)λ₯Ό ν΅ν΄ λμΌμ±μ μ μ§νλ©΄μ μνμ νλμ κ°μ§λ κ°μ²΄μ
λλ€. Entityλ λ¨μν λ°μ΄ν°λ₯Ό 보κ΄νλ κ΅¬μ‘°μ²΄κ° μλλΌ, μμ μ λ°μ΄ν°λ₯Ό μ§μ μ μ΄νκ³ κ΄λ¦¬νλ μν μ νλ©°, λλ©μΈ λ΄μμ μ€μν λΉμ¦λμ€ κ·μΉκ³Ό λ‘μ§μ ννν©λλ€.
μν νλ‘μ νΈμμλ Post, Comment, User λΌλ 3κ°μ μν°ν°λ‘ ꡬμ±λμ΄ μμ΅λλ€.
## Domain-driven Design(DDD)
ν΄λ¦° μν€ν
μ²λ DDDμ 곡ν΅μ μΌλ‘ λλ©μΈ μ€μ¬μ μ€κ³λ₯Ό μ§ν₯ν©λλ€. ν΄λ¦° μν€ν
μ²λ μννΈμ¨μ΄μ ꡬ쑰μ μ μ°μ±κ³Ό μ ν리μΌμ΄μ
μ μ μ§ λ³΄μ, κ·Έλ¦¬κ³ κΈ°μ μ λ
립μ±μ ν
μ€νΈ μ©μ΄μ±μ μ€μ μ λκ³ μμΌλ©° DDDλ λΉμ¦λμ€ λ¬Έμ ν΄κ²°μ μ΄μ μ λ§μΆκ³ μμ΅λλ€.
νμ§λ§ ν΄λ¦° μν€ν
μ²λ DDDμ μ² νκ³Ό μμΉμ μΌλΆ μ°¨μ©νκ³ μμΌλ©° DDDμ νΈνλλ©°, DDDλ₯Ό ν¨κ³Όμ μΌλ‘ μ μ©ν μ μμ΅λλ€. κ·Έλ¦¬κ³ κ·Έ μλ‘ ν΄λ¦° μν€ν
μ²λ DDDμ κ°λ
μΈ `Ubiquitous Language`μ `Aggregate Root`λ₯Ό νμ©ν μ μμ΅λλ€.
### Ubiquitous Language


μ λΉμΏΌν°μ€ μΈμ΄λ νλ‘μ νΈ μ λ°μ κ±Έμ³ μμ¬μν΅μ μΌκ΄μ±μ μ μ§νκΈ° μν΄ λͺ¨λ νμμ΄ μ¬μ©νλ 곡μ μΈμ΄λ₯Ό λ§ν©λλ€. μ΄ μΈμ΄λ νλ‘μ νΈ λ¦¬λ, λλ©μΈ μ λ¬Έκ°, κ°λ°μ, UI/UX λμμ΄λ, λΉμ¦λμ€ λΆμκ°, QA μμ§λμ΄ λ±μ ν¬ν¨ν λͺ¨λ νλ‘μ νΈ κ΅¬μ±μμ΄ κ³΅μ ν΄μΌ ν©λλ€. κ·Έλ¦¬κ³ μ΄ μΈμ΄λ νμ
μ€ λ¬Έμνλ λνμ μ¬μ©λ λΏλ§ μλλΌ μννΈμ¨μ΄ λͺ¨λΈκ³Ό μ½λμλ λ°μλμ΄μΌ ν©λλ€.
### Aggregate Root


Aggregateλ μ¬λ¬ μν°ν°μ κ° κ°μ²΄λ₯Ό ν¬ν¨ν μ μλ μΌκ΄μ± κ²½κ³λ‘, λ΄λΆ μνλ₯Ό μΊ‘μννμ¬ μΈλΆμμμ μ κ·Όμ μ μ΄ν©λλ€. λͺ¨λ μμ μ λ°λμ Aggregate Rootλ₯Ό ν΅ν΄μλ§ μ΄λ£¨μ΄μ§λ©°, μ΄λ λͺ¨λΈ λ΄μ κ΄κ³ 볡μ‘μ±μ κ΄λ¦¬νκ³ , μλΉμ€ νμ₯ λ° νΈλμμ
볡μ‘μ± μ¦κ° μ μΌκ΄μ±μ μ μ§νλ λ° λμμ΄ λ©λλ€.
μν νλ‘μ νΈμμλ Postκ° Aggregateλ‘ μ¬μ©λμμΌλ©° νμμλ μ’
μμ μΈ κ΄κ³μ Comment μν°ν°κ° μμ΅λλ€. κ·Έλ κΈ°μ Commentλ₯Ό μΆκ° λ° λ³κ²½νκΈ° μν΄μλ Postλ₯Ό ν΅ν΄μ μ΄λ£¨μ΄μ§λλ€. κ·Έλ¦¬κ³ Postμ μμ±μΌλ‘ μμ±μ μ¦, κΈμ μμ±ν μ¬μ©μμ μ λ³΄κ° νμνμ§λ§ Userλ λ
립μ μΈ μν°ν°μ΄κΈ° λλ¬Έμ μμ κ΄κ³λ₯Ό μ μ§νκΈ° μνμ¬ Userμ id κ°κ³Ό name μ 보λ§μ Value Objectλ‘ κ°μ§κ³ μμ΅λλ€.
## Use Cases
Use Caseλ μ¬μ©μμ μλΉμ€ κ°μ μνΈμμ©μ μ μνλ©°, λλ©μΈ κ°μ²΄(Entity, Aggregate, Value Object)λ₯Ό νμ©νμ¬ μλΉμ€κ° μ¬μ©μμκ² μ 곡ν΄μΌ νλ λΉμ¦λμ€ κΈ°λ₯μ λͺ
ννκ² ν©λλ€. μμ€ν
μν€ν
μ² κ΄μ μμ Use Caseλ μ ν리μΌμ΄μ
λ‘μ§κ³Ό λΉμ¦λμ€ κ·μΉμ λΆλ¦¬νλ μν μ νλ©°, μ§μ μ μΌλ‘ λΉμ¦λμ€ λ‘μ§μ μ μ΄ν기보λ€λ λλ©μΈ κ°μ²΄κ° κ°μ§ λΉμ¦λμ€ κ·μΉκ³Ό λ‘μ§μ νμ©ν μ μλλ‘ λμ΅λλ€.
μν νλ‘μ νΈμμλ κ°λ¨νκ² μ 체 μμ½ κΈ λ¦¬μ€νΈλ₯Ό κ°μ Έμ€κ±°λ κΈκ³Ό λκΈμ μΆκ°, μμ , λ³κ²½κ³Ό κ°μ κ°λ¨ν μνΈ μμ©μΌλ‘ ꡬμ±λμ΄ μμ΅λλ€.
## Inversion of Control


Repositoryμ κ²½μ° Adapter λ μ΄μ΄μ ν΄λΉνκΈ° λλ¬Έμ Use Caseμμλ Repositoryμ λν΄μ μμμλ μλ©λλ€. κ·Έλ κΈ° λλ¬Έμ Use Caseμμλ Repositoryλ₯Ό μΆμνν μΈν°νμ΄μ€λ₯Ό κ°μ§κ³ ꡬννλ©°, μ΄λ μ΄νμ `μμ‘΄μ± μ£Όμ
(DI: Dependency Injection)`λ₯Ό ν΅ν΄ λμν©λλ€.
# Adapters
Domains κ³μΈ΅κ³Ό μ μ¬νκ² Adatpers κ³μΈ΅λ λͺ¨λ
Έλ ν¬ λ΄μμ λ¨μΌ ν¨ν€μ§λ‘ ꡬμ±νμ¬ μ¬μ©ν©λλ€. Apapterμμλ μΌλ°μ μΌλ‘ Presenters, Repositories λ° Infrastructure κ΅¬μ± μμκ° ν¬ν¨λ©λλ€. μ΄λ¬ν κ΅¬μ± μμλ μμ‘΄μ± μ£Όμ
(DI)μ ν΅ν΄ μλΉμ€ ν¨ν€μ§μμ μ¬μ©λλ©° νμμ λ°λΌ μμνκ³ μ¬μ©μ μ μνμ¬ νμ₯ν μ μμ΅λλ€.
## Infrastructures
Infrastructure λ μ΄μ΄μμλ μΉ μλΉμ€μμ μΌλ°μ μΌλ‘ λ§μ΄ μ¬μ©νλ HTTPλ₯Ό μ¬μ©ν μΈλΆ μλ²μμ ν΅μ μ΄λ λλ LocalStorageμ κ°μ λΈλΌμ°μ μ Web APIμ κ°μ μ ν리μΌμ΄μ
μΈλΆμμ μ°κ²°μ κ΄λ¦¬ν©λλ€.
## Repositories
μΌλ°μ μΌλ‘ λ°±μλμμ Repository λ μ΄μ΄λ λ°μ΄ν°λ² μ΄μ€μ κ΄λ ¨λ `CRUD` μμ
μ μννλ©° λ°μ΄ν°μ μ μ₯, μ‘°ν, μμ , μμ μ κ°μ κΈ°λ³Έμ μΈ λ°μ΄ν° μ‘°μμ μ²λ¦¬ν©λλ€. κ·Έλ¦¬κ³ κ·Έλ¬ν λ°μ΄ν°λ² μ΄μ€μμ μνΈμμ©μ μΆμννμ¬ λΉμ¦λμ€ λ‘μ§μμ λ°μ΄ν° μ μ₯μμ λν΄ μ νμκ° μλλ‘ ν©λλ€.
κ°μ μλ¦¬λ‘ μν νλ‘μ νΈμμ Repository λ μ΄μ΄λ API μλ²μμ HTTP ν΅μ μ κ΄λ ¨λ POST, GET, PUT, DELETE μμ
μ μννλ©° κ·Έ μνΈμμ©μ μΆμννμ¬ λΉμ¦λμ€ λ‘μ§μμλ λ°μ΄ν°μ μΆμ²μ λν΄μ μ νμκ° μλλ‘ ν©λλ€. κ·Έλ¦¬κ³ μΈλΆ μλ²λ‘λΆν° λ°μ λ°μ΄ν°λ `DTO`λ‘ μΊ‘μννμ¬ μ΄ λ°μ΄ν°κ° ν΄λΌμ΄μΈνΈ λ΄λΆμμ μ¬μ©λ λμ μμ μ±μ 보μ₯ν©λλ€.
## Presenters
Presenters λ μ΄μ΄μμλ UIμμ νμλ‘νλ λ©μλλ₯Ό κ°μ§κ³ μ¬μ©μμ μμ²μ μλ²λ‘ μ λ¬νλ μν μ νλ©°, μμ²μ λ°λΌ μν°ν° λ°μ΄ν°λ₯Ό UIμμ μ¬μ©λλ View Modelλ‘ κ°μ λ³ννμ¬ μλ΅νλ μν μ νκΈ°λ ν©λλ€.
## Dependency Injection


κ°κ°μ λ μ΄μ΄λ μ΅μ’
μ μΌλ‘ μμ‘΄μ± μ£Όμ
(Dependency Injection)μ ν΅ν΄ λμν©λλ€. μλ‘ λ€λ©΄, κ° λ μ΄μ΄λ³λ‘ μΈν°νμ΄μ€λ₯Ό μ μνκ³ μ΄ μΈν°νμ΄μ€λ₯Ό κΈ°λ°μΌλ‘ λ€μν ꡬν체λ₯Ό λ§λ€μ΄ νμμ λ°λΌ μ£Όμ
νμ¬ μ¬μ©ν©λλ€.
μν νλ‘μ νΈμμλ Repository μΈν°νμ΄μ€λ₯Ό μ μνκ³ , μ΄λ₯Ό ꡬννλ NetworkRepository(HTTP ν΅μ )μ StorageRepository(μΉ μ€ν λ¦¬μ§ μ¬μ©)λ₯Ό ꡬμ±ν λ€, μλΉμ€μ λ°λΌ μ νμ μΌλ‘ μ£Όμ
νμ¬ μ¬μ©νκ³ μμ΅λλ€.
μ΄μ²λΌ μμ‘΄μ± μ£Όμ
μ ν΅ν μλΉμ€ ꡬμ±μ μν κ³Ό μ±
μμ λͺ
νν λλμ΄ μμ λ²μλ₯Ό μ΅μνν μ μμΌλ©°, μΆμνμ μμ‘΄νλ μ€κ³λ₯Ό ν΅ν΄ μλ‘μ΄ κ΅¬ν체λ₯Ό μΆκ°νμ¬ λμ νμ₯μ±κ³Ό μ μ°μ±μ κ°μ§ μλΉμ€λ₯Ό κ°λ°ν μ μμ΅λλ€.
> μΌλ°μ μΌλ‘ HTTP ν΅μ κ³Ό μΉ μ€ν 리μ§λ κ°κΈ° λ€λ₯Έ μν μ νλ―λ‘, μν νλ‘μ νΈμ²λΌ λ ꡬν체λ₯Ό κ°μ λμμΌλ‘ μ μνκ³ μ νμ μΌλ‘ μ¬μ©νλ κ²½μ°λ λλ
λλ€. μ΄ μμλ λ¨μν λ€μν ꡬν체λ₯Ό μ μνκ³ κ·Έ μ°¨μ΄λ₯Ό 보μ¬μ£ΌκΈ° μν λͺ©μ μΌλ‘ μ¬μ©λμμ΅λλ€.
# Services
μν νλ‘μ νΈμ ν΄λΌμ΄μΈνΈ μλΉμ€λ Client-A, Client-B μ΄λ κ² 2κ°μ κ°λ¨ν μλΉμ€λ‘ ꡬμ±λμ΄ μμ΅λλ€. λ μλΉμ€ λͺ¨λ λμΌν λλ©μΈ κΈ°λ°μ μλΉμ€λ‘ UI μ»΄ν¬λνΈλ [Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/)μ κΈ°λ°μΌλ‘ μ€κ³νμμ΅λλ€.
## Client-A
### Use Stack
```
Vite, React, Jotai, Tailwind CSS, Jest, RTL, Cypress
```
Client-Aλ `Domains`μ `Adapters` λ μ΄μ΄μ μμλ€μ κ·Έλλ‘ μ¬μ©ν΄μ μ΅μ’
μ μΌλ‘ `DI`λ μμ λ μ΄μ΄μ κ°μ²΄λ₯Ό Reactμ Hooksμ μ μ μν λΌμ΄λΈλ¬λ¦¬μΈ [Jotai](https://jotai.org/)λ₯Ό νμ©νμ¬ κ° λλ©μΈμ λ©μλλ₯Ό ꡬννκ³ μ΄λ Presenters λ μ΄μ΄μ μν μ μνν©λλ€.
> κΈ°μ‘΄μ Adapters ν¨ν€μ§μμ Presenters λλ ν λ¦¬λ‘ λͺ
μμ μΌλ‘ Presenters λ μ΄μ΄λ₯Ό λλμμ§λ§ μ΄λ νλ μμν¬μ μμ‘΄νμ§ μμ λ²μ©μ μΈ Presentersμ΄λ©°, μ μν νλ‘μ νΈμ²λΌ Reactλ₯Ό μ¬μ©νλ μλΉμ€μμλ κ·Έμ λΆν©νλ ꡬμ±μ μν΄μ μ΅μ’
μ μΌλ‘ μμ‘΄μ±μ μ£Όμ
ν Presenters κ°μ²΄μ React Hooksμ νμ©νμ¬ Presenters μμμ νμ₯ ꡬμ±νμμ΅λλ€.
### Dependency Injection
```tsx
import { API_URL } from "../constants"
import infrastructuresFn from "./infrastructures"
import repositoriesFn from "./repositories"
import useCasesFn from "./useCases"
import presentersFn from "./presenters"
export default function di(apiUrl = API_URL) {
const infrastructures = infrastructuresFn(apiUrl)
const repositories = repositoriesFn(infrastructures)
const useCases = useCasesFn(repositories)
const presenters = presentersFn(useCases)
return presenters
}
```
### Presenters
```tsx
import { useCallback, useMemo, useOptimistic, useState, useTransition } from "react"
import { atom, useAtom } from "jotai"
import presenters from "../di"
import PostVM from "../vms/PostVM"
import IPostVM from "../vms/interfaces/IPostVM"
const PostsAtoms = atom([])
export default function usePosts() {
const di = useMemo(() => presenters(), [])
const [post, setPost] = useState(null)
const [posts, setPosts] = useAtom(PostsAtoms)
const [optimisticPost, setOptimisticPost] = useOptimistic(post)
const [optimisticPosts, setOptimisticPosts] = useOptimistic(posts)
const [isPending, startTransition] = useTransition()
const getPosts = useCallback(async () => {
startTransition(async () => {
const resPosts = await di.post.getPosts()
const postVMs = resPosts.map((post) => new PostVM(post))
setPosts(postVMs)
})
}, [di.post, setPosts])
...
}
```
### View Models
Client-Aμμλ νλ‘μ νΈ λ μ΄μ΄μμ Reactμ UI μν κ΄λ¦¬μ μ ν©νλλ‘ View Modelμ ꡬμ±νμ¬ μ¬μ©νμμ΅λλ€.
```ts
import CryptoJS from "crypto-js"
import IUserInfoVO from "domains/vos/interfaces/IUserInfoVO"
import ICommentVM, { ICommentVMParams } from "./interfaces/ICommentVM"
export default class CommentVM implements ICommentVM {
readonly id: string
readonly postId: string
readonly author: IUserInfoVO
readonly createdAt: Date
key: string
content: string
updatedAt: Date
constructor(parmas: ICommentVMParams) {
this.id = parmas.id
this.postId = parmas.postId
this.author = parmas.author
this.content = parmas.content
this.createdAt = parmas.createdAt
this.updatedAt = parmas.updatedAt
this.key = this.generateKey(this.id, this.updatedAt)
}
updateContent(content: string): void {
this.content = content
this.updatedAt = new Date()
this.key = this.generateKey(this.id, this.updatedAt)
}
applyUpdatedAt(date: Date): void {
this.updatedAt = date
this.key = this.generateKey(this.id, this.updatedAt)
}
private generateKey(id: string, updatedAt: Date): string {
const base = `${id}-${updatedAt.getTime()}`
return CryptoJS.MD5(base).toString()
}
}
```
View Modelμμλ μμ κ°μ΄ κ° λ³κ²½μ λ°λ₯Έ λ©μλλ₯Ό μ 곡νλ©°(e.g., updateContent) λͺ¨λ λ³κ²½μλ updatedAt κ°μ΄ ν¨κ» λ³κ²½νκ³ , updatedAt κ°κ³Ό ID κ°μ νμ©νμ¬ κ³ μ ν `Key` κ°μ λ§λ€μ΄ μ¬μ©ν¨μΌλ‘μ¨ Reactκ° Viewμ λ³κ²½μ κ°μ§νκ³ λ¦¬λ λλ§ ν μ μλλ‘ νμμ΅λλ€.
```tsx
...
export default function usePosts() {
...
const deleteComment = useCallback(
async (commentId: string) => {
startTransition(async () => {
setOptimisticPost((prevPost) => {
prevPost.deleteComment(commentId)
return prevPost
})
try {
const isSucess = await di.post.deleteComment(commentId)
if (isSucess) {
const resPost = await di.post.getPost(optimisticPost.id)
const postVM = new PostVM(resPost)
setPost(postVM)
}
} catch (e) {
console.error(e)
}
})
},
[di.post, optimisticPost, setOptimisticPost, setPost]
)
...
}
```
Presenter λ μ΄μ΄μ Hooksμμλ μμ κ°μ΄ Commentμ μμ μμ²μ λν κ°λ¨ν μμλ‘, VMμμ μ 곡νλ λ©μλλ₯Ό νμ©νμ¬ λκ΄μ μ
λ°μ΄νΈλ₯Ό ꡬννκ³ μμ²μ΄ μ±κ³΅νλ©΄ μ λ³κ²½μ΄ μ μ©λ μλ‘μ΄ λ°μ΄ν°λ₯Ό μμ²νμ¬ λκΈ°ν νλλ‘ νμμ΅λλ€.
## Client-B
### Use Stack
```
Next.js, Jotai, Tailwind CSS, Jest, RTL, Cypress
```
Client-Bλ Client-Aμ λμΌν λλ©μΈμ νμ©ν, μλΉμ€ νμ₯μ νννλ μλΉμ€λ‘ Client-A μλΉμ€μ μ μ¬νμ§λ§ Client-A μλΉμ€μ λ€λ₯΄κ² Next.jsλ₯Ό κΈ°λ°μΌλ‘ νλ©° κΈ°μ‘΄μ Client-A μλΉμ€λ API μλ²μμ HTTP ν΅μ μ ν΅ν΄ λ°μ΄ν°λ₯Ό μ‘°μνμ§λ§ Client-Bλ HTTP ν΅μ μμ΄ λ‘컬 μ μ₯μ(Local Storage)λ₯Ό κΈ°λ°μΌλ‘ λμνλ μ°¨μ΄κ° μμ΅λλ€.
Client-Aμ κ°μ `Domains` λ μ΄μ΄μ `Adpaters` λ μ΄μ΄μμ μ μν μΈν°νμ΄μ€μ ꡬν체λ₯Ό νμ©νμ¬ λμ μ½λ μ¬μ¬μ©μ±μ κ°μ§κ³ ꡬμ±ν μ μμ΅λλ€. λν, μμ‘΄μ± μ£Όμ
(DI) κ³Όμ μμ HTTP ν΅μ μ μ¬μ©νλ Repositoryλ₯Ό λμ νμ¬ μΉ μ€ν 리μ§λ₯Ό μ¬μ©νλ Repositoryλ₯Ό μ¬μ©νλ κ²λ§μΌλ‘ κ°λ¨νκ² μλ‘μ΄ μλΉμ€λ₯Ό ꡬνν μ μμ΅λλ€.
> Client-Bλ ꡬμ μ μΈ κΈ°λ₯ ꡬν보λ€λ λμΌν λλ©μΈμ νμ©ν λ€λ₯Έ ν΄λΌμ΄μΈνΈ μλΉμ€ ꡬμ±μ λν κ°λ¨ν μμμ
λλ€.
## Design System
μν μλΉμ€μ²λΌ κ° μλΉμ€κ° λμΌν νλ μμν¬λ₯Ό μ¬μ©νλ€λ©΄, λͺ¨λ
Έλ ν¬ κ΅¬μ±μ μ₯μ μ νμ©νμ¬ κ³΅ν΅μΌλ‘ μ¬μ© κ°λ₯ν UI μ»΄ν¬λνΈλ₯Ό λ³λμ ν¨ν€μ§λ‘ ꡬμ±ν¨μΌλ‘μ¨ μ»΄ν¬λνΈμ μ¬μ¬μ©μ±μ λμ¬ λμ± ν¨κ³Όμ μΌλ‘ μλΉμ€λ₯Ό νμ₯ λ° μ μ§ λ³΄μν μλ μμ΅λλ€.
# μ€ν
μν νλ‘μ νΈλ 루νΈμ λ±λ‘λ 컀맨λλ₯Ό νμ©νμ¬ κ° ν¨ν€μ§λ₯Ό λΉλ λλ μ€νν μ μμ΅λλ€.
## μ€μΉ
```sh
$ yarn install
```
## μ€ν
```sh
# client-a
$ yarn start:a
# client-b
$ yarn start:b
```
# Thank You!
λͺ¨λ μ§μκ³Ό κ΄μ¬μ κ°μ¬λ립λλ€. πββοΈ