react-with-clean-architecture
react-with-clean-architecture copied to clipboard
Clean architecture based react project sample code.
Sample code of React with Clean architecture
This project is a small idea sample code to introduce a Clean Architecture to a web service or to use a Redux Architecture and a Clean Architecture together.
if you leave an issue or a pull request, we will reflect the insufficient part or improvement. βΊοΈ
(+ i am not good at English.)
Language
Use Stack
Typescript, Webpack, React, React-Native, Recoil, Styled-Components
(Recoil > Redux)
https://github.com/falsy/react-with-clean-architecture/tree/v1.8.1
Clean Architecture
As with various architectures, the primary purpose of a clean architecture is to separate concerns. Divide the hierarchy according to each interest, design domain-centric rather than detailed implementation, and make sure that the internal area does not depend on external elements such as the framework or database UI.
- Distinguish between detailed implementation areas and domain areas.
- Architecture does not depend on the framework.
- The outer zone can depend on the inner zone, but the inner zone cannot depend on the outer zone.
- Both high-level and low-level modules rely on abstraction..
Communitaction Flow
in simple diagram, it is as above.
Session
After the user logs in, the issued authentication token is stored and used in the web storage. web storage is accessible globally, but the sample code follows the flow above and is controlled by 'Storage' in 'Infrastructures'. this is part of a detailed implementation that can change, and is positioned according to its role to improve maintenance.
Board
Board posts and comments are fetched through http communication from 'Infrastructures', encapsulated as Board Root Entity including Comment Entity in 'Use Case' and delivered to 'Presenter', and 'Presenter' returns Entity data.
in 'Components', 'Entity' data or 'View Model' encapsulated data is stored in the state management manager, and the view is redrawn according to the state change of the data.
Inversion of Control
In the case of 'Repository', it is an adapter layer, so you should not know about 'Repository' in 'Use Case'. Therefore, in 'Use Case', it is implemented through the Repository Interface located in the domain layer, which is then operated through Dependency Injection.
Directory Structure
./src
ββ adapters
β ββ infrastructures
β β ββ interfaces
β ββ presenters
β β ββ interfaces
β ββ repositories
ββ domains
β ββ aggregates
β β ββ interfaces
β ββ entities
β β ββ interfaces
β ββ useCases
β β ββ interfaces
β β ββ repository-interfaces
β ββ dto
ββ frameworks
ββ web
β ββ di
β ββ components
β ββ hooks
β ββ vm
ββ mobile(React Native)
ββ di
ββ components
ββ android
ββ ios
ββ hooks
ββ vm
- The basic directory is organized based on layers of clean architecture.
[ frameworks / adapters / domains(useCases / entities) ] - The component's directory structure is freely structured in the form promised between services or members.
Screenshots
Alias
Web
tsconfig.json
/src/frameworks/web/tsconfing.json
{
"compilerOptions": {
//...
"baseUrl": ".",
"paths": {
"@adapters/*": ["../../adapters/*"],
"@domains/*": ["../../domains/*"],
"@frameworks/*": ["../../frameworks/*"],
"@di": ["./di/index.ts"]
}
},
}
webpack.config.js
/src/frameworks/web/webpack.config.js
{
//...
resolve: {
extensions: [".tsx", ".ts", ".js"],
alias: {
"@adapters": path.resolve(__dirname, "../../adapters/"),
"@domains": path.resolve(__dirname, "../../domains/"),
"@frameworks": path.resolve(__dirname, "../../frameworks/"),
"@di": path.resolve(__dirname, "./di/index.ts")
}
},
}
Mobile
tsconfig.json
/src/frameworks/mobile/tsconfing.json
{
"compilerOptions": {
//...
"baseUrl": ".",
"paths": {
"@adapters/*": ["../../adapters/*"],
"@domains/*": ["../../domains/*"],
"@frameworks/*": ["../../frameworks/*"],
"@di": ["./di/index.ts"]
}
},
}
metro.config.js
/src/frameworks/mobile/metro.config.js
const path = require('path')
const extraNodeModules = {
'@adapters': path.resolve(__dirname + './../../adapters'),
'@domains': path.resolve(__dirname + './../../domains'),
'@frameworks': path.resolve(__dirname + './../../frameworks'),
}
const watchFolders = [
path.resolve(__dirname + './../../adapters'),
path.resolve(__dirname + './../../domains'),
path.resolve(__dirname + './../../frameworks'),
]
module.exports = {
//...
resolver: {
extraNodeModules: new Proxy(extraNodeModules, {
get: (target, name) =>
name in target ? target[name] : path.join(process.cwd(), `node_modules/${name}`),
}),
},
watchFolders,
}
Run Project
1. Mock Server
Install
# $ cd /mock-server
$ npm install
Start
# $ cd /mock-server
$ npm start
2-1. Web
Install
# $ cd /src/frameworks/web
$ npm install
Start
# $ cd /src/frameworks/web
$ npm start
2-2. Mobile(ios)
Install
# $ cd /src/frameworks/mobile
$ npm install
# cocoapods install
$ gem install cocoapods
# $ cd /src/frameworks/mobile/ios
$ pod install
Start
# $ cd /src/frameworks/mobile
$ npx react-native run-ios
Version
v1.9.0 - ChangeLog