The purpose of this project is to serve as an example that shows how to structure a Nodejs API, using Express framework, into layers to compose a maintainable project, keeping framework, application, and domain isolated from each other.
- Install the dependencies
npm install
- Run in develop mode
Run the API npm run start_api
Run cron jobs npm run start_cron
Or run both using PM2, pm2 start ecosystem.config.cjs
It's required to have NodeJS, preferably the latest stable version. To install NodeJS, is suggested to use https://github.com/nvm-sh/nvm.
adapters
Code that integrates application with external tools (database drivers, email integration, notification, other services, cron jobs), an adapter in the sense of the word. This code is not part of the application but is required by it to execute some commands. Adapters are injected into the domain to decouple the dependency.
boot
Contain functions that initialize and configure the application, such as configure the error handler, load routes, apply global middlewares, configure the view engine, and load application module providers.
config
All global application configurations are available in this directory. New global configurations can be added as required.
resources
This directory holds all assets (styles, media, client scripts) required when working with views, also all files that are available to download.
It's well known that that kind of files should be in a CDN server, and files to download in a storage server. The intention is to show how it is possible to serve files directly from the server.
src
This is the most important directory, it's where the application code stands. The directory has multiple subdirectories, and each one is a module that answer for a specific application context. Some modules (04 of them) are not related to application itself, are support modules (or core modules).
The core module provides resources to the application (events, exceptions, notifier, repository decoupling, view render), they act as interfaces providing decoupling to the domain from adapters.
The support module has global helpers.
The logger module exports functions that helps to registry log messages (errors, infos, warning, etc);
The last one, maintenance, is responsible to define how the application responds to a not defined route and health check router.
All others subdirectories are specific application logic, in this project sample, account, artist and auth are application domains.
Each module also follows a convention:
- in the root of module there is a
routesfile, which exports all available routes of this module. This routes are loaded byboot/providers/RouteProvider; - a folder named
Providerexport any initialization that is required to set up the module. This provider is loaded byboot/appand run at the application boot; - the
controllerholds the functions/controllers that handle the requests to each route; - the
actionsdirectory contains the ports for the application, they are called bycontrollersto execute a specific task in the domain; - when some logic is required by an
action, and that logic is not related to solving some business problem directly, it's up to anapplication_serviceto implement that logic; - the
entitieshave all domain entities (class or functions); - the
eventscontains all events emitted by the application or domain, also the listeners for that events; - the
presenterscontains functions to format the output, they are used only by controllers; - the
repositoriesdirectory holds all repositories implementation required by its module. By implementation means to have the methods to save and query data, also work with collections. To save and query data, it uses a specific implementation that is provided to its constructor, so the save and query are just abstractions. - when working with views, all view templates and assets are put in
viewsdirectory; - tasks that needs to run frequently (cron jobs) are stored in
jobsfolder.
test
All tests are in this directory, it follows the same folder structure as src, except it don't need all subfolders that are inside each module.
In order to run the app in production mode, is required some process manager (PM2) to start the application and keep it running. For that, there is an initialization file ecosystem.config where is defined the application name, entry point, exec mode, besides other configs.