A full-stack Todo List application demonstrating three different backend architectures: Monolith, Layered, and Microservices. The frontend is a React app using Vite, TypeScript, Tailwind, and Shadcn. This is an educational project to learn about different backend architectures.
- Frontend: React, Vite, TypeScript, Tailwind, Shadcn
- Monolith Backend: Node.js, Express, TypeScript, Drizzle, PostgreSQL
- Layered Backend: Node.js, Express, TypeScript, Drizzle, PostgreSQL
- Microservices Backend:
- API Gateway: Node.js, Express, TypeScript, http-proxy-middleware
- Users Service: Node.js, Express, TypeScript, Drizzle, PostgreSQL
- Todos Service: Node.js, Express, TypeScript, Drizzle, PostgreSQL
- Database: PostgreSQL (shared but could be separate)
- API Documentation: Swagger
todo-list-multi-architecture/ frontend/ # React app (Vite, Tailwind, Shadcn) monolith-backend/ # Monolithic backend (single codebase) layered-backend/ # Layered backend (separated by layers) microservices-backend/ api-gateway/ # API Gateway (routes requests to services) users-service/ # User microservice todos-service/ # Todos microservice package.json # Monorepo scripts - React App: User interface built with React, styled using Tailwind and Shadcn.
- API Layer (Axios): Handles all HTTP requests to the backend. The frontend can target any backend architecture (monolith, layered, or microservices) via a selector.
- Single Express app: All features (users, todos) in one file.
- Structure: Controllers, models, routes, middleware, etc. in a single file.
- Drizzle: Type-safe DB access.
- PostgreSQL: Database.
- Layered Express app: Codebase separated by layers (controllers, services, repositories, models, etc.).
- Improved separation of concerns: Each layer has a clear responsibility.
- Drizzle: Type-safe DB access.
- PostgreSQL: Database.
- API Gateway: Single entry point, proxies requests to services based on route:
/api/users→ Users Service/api/tasks→ Todos Service
- Users Service: Handles user registration, login, and user data.
- Todos Service: Handles todo/task CRUD, validates user existence via Users Service.
- Drizzle: Type-safe DB access in each service.
- PostgreSQL: Database.
- Swagger: API docs for each service.
- Node.js (v20+ recommended)
- PostgreSQL database (I used Docker to spin up a local instance for development)
git clone https://github.com/your-username/todo-list-multi-architecture.git cd todo-list-multi-architectureInstall dependencies for each backend and frontend with:
npm install- Copy
.env.example(if available) or create.envfiles in each backend directory (monolith-backend/,layered-backend/,microservices-backend/api-gateway/,microservices-backend/users-service/,microservices-backend/todos-service/). - Set the following variables as needed:
- Frontend:
VITE_API_URL(if needed) - Monolith Backend:
DATABASE_URL,FRONTEND_URL - Layered Backend:
DATABASE_URL,FRONTEND_URL - Microservices:
- API Gateway:
FRONTEND_URL,USERS_SERVICE_URL,TODOS_SERVICE_URL - Users Service:
DATABASE_URL,FRONTEND_URL - Todos Service:
DATABASE_URL,FRONTEND_URL,USERS_SERVICE_URL
- API Gateway:
- Frontend:
Example for microservices-backend/api-gateway/.env:
FRONTEND_URL=http://localhost:5173 USERS_SERVICE_URL=http://localhost:5001 TODOS_SERVICE_URL=http://localhost:5002 If using different databases for each backend:
# Monolith cd monolith-backend npx drizzle-kit push cd .. # Layered cd layered-backend npx drizzle-kit push cd .. # Microservices cd microservices-backend/users-service npx drizzle-kit push cd ../todos-service npx drizzle-kit push cd ../../..If using the same database for all backends (just run this once):
cd monolith-backend npx drizzle-kit pushFrom the root:
npm run dev- This will start all services concurrently (if configured in the root
package.json):- Frontend: http://localhost:5173
- Monolith Backend: http://localhost:3000
- Layered Backend: http://localhost:4000
- Microservices API Gateway: http://localhost:5000
- Microservices Users Service: http://localhost:4001
- Microservices Todos Service: http://localhost:4002
You can run each backend independently using their own scripts.
Visit http://localhost:5173 in your browser. (default)
npm run dev— Start all services in development modenpm run dev:frontend— Start frontend onlynpm run dev:monolith— Start monolith backend onlynpm run dev:layered— Start layered backend onlynpm run dev:microservices— Start all microservices (API Gateway, Users, Todos)npm run dev:api-gateway— Start API Gateway onlynpm run dev:users-service— Start Users Service onlynpm run dev:todos-service— Start Todos Service only
- Monolith Backend Swagger UI: http://localhost:3000/api/docs
- Layered Backend Swagger UI: http://localhost:4000/api/docs
- Microservices Users Service Swagger UI: http://localhost:5001/docs
- Microservices Todos Service Swagger UI: http://localhost:5002/docs
- I developed each backend separately, started with monolith, then refactored to layered, then microservices.
- The frontend can switch between architectures.
- add a docker compose file to run each backend in a container
- deploy backends to railway and frontend to vercel
- Tracing for the different backends to see the flow of requests
- jwt auth for authentication
- make this a production ready app (CI/CD, monitoring, logging, etc.)