Clean Architecture is an architecture design developed by Robert C. Martin (Uncle Bob). You will find many tutorials talking about the theory and concepts behind Clean Architecture. How you choose to apply these concepts in your project is up to you. You will see in this article, some variations of this design.
With all these concepts, I wanted to provide a real-world example using best practices applying Clean Architecture.
The application fetches number trivia from Numbers API when connected to the internet (online). If the application detects a network connection is no longer present (offline), it will fetch the last trivia from local cache. You can fetch trivia from a number you provide or a random number provided by the API. Because the business logic independent of any presentation or frameworks, applications representing the presentation layer can be built using different UI framework and adapters. This project provides some concrete implementations using React.js and Vue.js.
This project was built using Turborepo. This turborepo uses pnpm as a package manager. It includes the following packages/apps:
apps/react
: a React.js appapps/vue
: a Vue.js appapps/server
: a Node.js web service. See Readmepackages/business
: business logic demonstrating how to implement thedomain
anddata
layers of clean architecture.packages/eslint-config-custom
:eslint
configurations (includeseslint-config-next
andeslint-config-prettier
)packages/tsconfig
:tsconfig.json
s used throughout the monorepo
Each package/app is 100% TypeScript.
To demonstrate a clean presentation layer, the web applications use Shoelace, a web component library built on Lit. Developing web components is a best practice when you want to apply the DRY (Don't repeat yourself) principle.
Custom adapters were developed that leverage our business logic but meet the requirements needed by the server. See Readme
The demo is built using TDD ensuring all business logic passes and has coverage. Tests were built and tested using Vitest. Vitest is built on top of Jest. If you know Jest, you know Vitest. Vitest supports TypeScript by default. Here is a great overview video on Vitest: https://www.youtube.com/watch?v=7f-71kYhK00
To install all apps and packages dependencies, run the following command from the project root:
To build all apps and packages, run the following command:
pnpm run build
pnpm install
To test all apps and packages, run the following command:
pnpm run test
The test will be reported in the terminal and in HTML packages/business/coverage/index.html
.
To develop all apps and packages, run the following command:
pnpm run dev
This turborepo has some additional tools already setup for you:
- TypeScript for static type checking
- ESLint for code linting
- Prettier for code formatting
-
In order to get the
business
package to work with the server and web applications, storage needed to beasync
. An important note when building things is its better to go async route initially to avoid reworking code later. -
Getting the dev and build environments to play with each other was a real pain. The web apps use
vite
while the server uses just the TypeScript compiler. Neither the vite nor the TS docs helped with this issue. I did read thoroughly this section in the Vite docs - https://vitejs.dev/guide/build.html#building-for-production. But what it really came down to was that I had some luck with the compiling by placing this in thevite.config.ts
file:
optimizeDeps: {
include: [
"business",
],
},
build: {
rollupOptions: {}
},
Notice that the build.rollupOptions just contains an empty object. I don't know why but this works along with the optimizeDeps. I don't like that there are some unknowns there at the moment but the project will run and build all projects correctly.
- Provide a application example in React.
- Provide a application example in Vue.
- Provide a service example using Node.js.
- Provide a command line example that can be run from the terminal.
- Provide a desktop example using Electron.
This project is inspired by Flutter tutorials by Matt Rešetár
MIT license.