Node.js template for Service-oriented architecture
Don't forget to setup Redis and Postgres before start. Use docker for quick run of redis and postgres.
Example docker redis and postgers:
# Redis
sudo docker run -p 6379:6379 --name redis_stack_dev -d redis/redis-stack-server:latest
# Postgres
docker run --name pg_dev -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -d postgres
- Create .env file and write env varibale DATABASE_URL example of env file:
DATABASE_URL=postgresql://postgres:[email protected]:5432/test
- Run commands
npm ci
npm run build
npx prisma migrate dev --name init
Template consist of:
- Infrastructural services (
src/infra/
) - Application and domain services (
src/services/
) - API layer (
src/api
) - Server with its plugins as a separate unit
Built using:
fastify
and its ecosystem - https://www.fastify.io/prisma
- https://www.prisma.io/redis
- https://redis.io/
Above dependencies can be replaced, but it will probably require some effort since they contain a lot of out-of-the-box functionality, that aren't always present in their alternatives.
Contains services required by domain and application services to do their job. Database connections, logging, messsaging, external API clients - all are valid examples of Infrastructural services. Those services contain no logic and as result can be reused on any project.
Important part of infrastructure as it helps to decouple different parts of application
(as a midiator) and also handles communication between services. Implements Pub/Sub
and
Command
interfaces allowing RPC-like calls and handling of events. Additonally contains
methods for handling schemas and injecting metadata.
Has 2 implementations - local
(using EventEmitter
) and distributed
(using redis
)
for ability to start from monolith and later smoothly transition to microservices.
This layer contains all app specific logic. Each service consist of commands
and eventHandlers
implemented using domain function like style where each function receives infra
as a first
argument and actual payload as a second. Infrastructe gets injected into each function using
partial application.
Services should know only about Infrastructure (interfaces) and lib
(utility functions, commun
schemas etc...).
src/services/error.js
contains general ServiceError
that should be used inside services.
Those errors are caught and processed as expected errors. All other errors are treated as
internal errors so they cannot be used to communicate to end user.
src/services/types.d.ts
contains Command
and EventHandler
generic types as well as typings
for services supporting functions.
src/services/services.js
contains logic to initialize services - inject infra
, wrap into helper
function and register them to bus
.
Consist of named commands, each of which consist of auth
, input
, output
schemas and handler
.
auth
schema can contain any metadata to define access to current service function (e.g. list
of allowed roles or required permissions) - this data will be passed to auth
service's verify
function along with users data to check access rights.
input
and output
are used to validate payload
and results, described using JSONSchema
allowing getting types from it and easy integration into fastify
's ecosystem.
handler
- service function.
Consist of eventName
eventHandler
pairs. Each eventHandler
is a service function.
This layer maps service commands to external users. Currently supports HTTP requests only.
Each endpoint definition implements HTTPRoute
or HTTPRouteRaw
.
First one is simple mapping of HTTP's url
, method
and inputSource
to a command
.
Second one is used when more flexibility required - it has access to req
/res
objects
inside its handler
with bus
injected.
Contains fastify
HTTP and WebSocket (currently only for notification like messages) servers.
Additionally has multiple plugins from fastify
's ecosystem (cors, swagger, auth...) as well as
custom plugins for transforming HTTP mapping into fastify
route, handling custom auth and
WebSockets. Currently, custom-websocket
only emits WS open/close events with server
and
websocket
meta and subscribes to new WS message events for current instance of the server.