This is a sample project demonstrating a pragmatic monolith architecture for web applications.
This project features a simple marketplace where users can buy and sell widgets. Domain specific logic is intentionally minimal, existing only to demonstrate the broader concepts. The point of this example is to show a possible architecture for small to mid-sized applications that is easy to understand, easy to maintain, and easy to scale. To that end, this architecture incorporates a strict separation of concerns enabling an engineering team to gradually break off components from the original application into independent applications as bottlenecks become apparent through stress testing or production usage. The overall goal of this design is to start simple but retain the flexibility to grow in the direction needed as an application scales.
This is a simple application. It is not intended to include every feature and security measure required of a real world application. Accordingly, the following design considerations are in effect:
- Every user is honest.
- No authentication.
- No authorization.
- No protected API routes.
- No accounting for users inappropriately modifying users or widgets.
- No scrubbing external data before feeding it into the application.
- No keeping the .env environment file a secret.
- Every user is careful.
- No checking API requests for the correct data.
- No checking API requests for malformed content.
- Traffic levels will start small and have no spikes.
- A monolithic architecture is suitable.
- A lightweight database is suitable.
- No database connection pools.
- No database sharding, replication, or read/write splits.
- Not all code needs comments.
- Simple code can stand on its own.
- Well-tested code can stand on its own.
- Code for "internal" audiences does not always need as much documentation as code for "external" audiences.
This project includes "Architecture Decision Records" (ADR) to document its structure. These are the decisions that "affect the structure, non-functional characteristics, dependencies, interfaces, or construction techniques" of the application (see "Documenting Architecture Decisions" by Michael Nygard). For the design decisions affecting this project, see docs/adr.
Installation is unnecessary for the purposes of this example project. However, this is still a working application that interested developers can run if desired. Follow these steps to get started:
- Install the latest LTS version of Node.js.
- Navigate to the root directory of this project.
- Run
npm install
from the console.
- Run
npm start
to launch the production server. - Run
npm run start:dev
to launch the development server.
Note: The server does NOT need to be running to run these tests.
- Run
npm test
to run all tests. - Run
npm run test:standard
to run all tests that do not require the database. - Run
npm run test:db
to run all tests that do require the database.
Note: The server DOES need to be running to run these commands.
Use these curl commands to probe the different API routes found in this application:
# Users:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"first_name":"Babe", "last_name":"Ruth", "email":"[email protected]"}' \
http://localhost:8080/users
curl --header "Content-Type: application/json" \
--request POST \
--data '{"first_name":"Ryan", "last_name":"Laukat", "email":"[email protected]"}' \
http://localhost:8080/users
curl --header "Content-Type: application/json" \
--request PATCH \
--data '{"balance":10.00}' \
http://localhost:8080/users/2
curl http://localhost:8080/users/1
curl http://localhost:8080/users/2
# Widget:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"id_seller":1, "description":"A very nice widget", "price":5.75}' \
http://localhost:8080/widgets
curl http://localhost:8080/widgets/1
# Transaction:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"buyer_id":2}' \
http://localhost:8080/widgets/1
- dotenv: Provides a framework to configure the application environment
- express: Common Node.js API framework
- neverthrow: Expressive type framework to handle possible error results
- SQLite database: File-based, SQL-compliant database perfect for prototyping
- sqlite3: Database driver for SQLite
- sqlite: Works with sqlite3 to enable promise chaining and database migrations
- @types/express: Provides TypeScript definitions for Express.js
- @types/node: Provides TypeScript definitions for Node.js
- @types/sqlite3: Provides TypeScript definitions for the sqlite3 database driver
- ava: Minimalist test runner for Node.js applications
- prettier: Opinionated code formatter to enforce consistent style choices
- ts-node: Provides an environment to run TypeScript code without the need to compile it first
- typescript: Compiler to support the TypeScript language and statically typed JavaScript