GithubHelp home page GithubHelp logo

talyssonoc / node-api-boilerplate Goto Github PK

View Code? Open in Web Editor NEW
3.3K 87.0 540.0 674 KB

DDD/Clean Architecture inspired boilerplate for Node web APIs

Home Page: https://github.com/talyssonoc/node-api-boilerplate/wiki

License: MIT License

TypeScript 97.83% Shell 2.17%
boilerplate ddd clean-architecture nodejs scalability good-practices hacktoberfest

node-api-boilerplate's Introduction

What is it

This project is a starting point for you to develop a web API in a scalable way with Node and TypeScript, and was implemented following ideas from layered architecture, Clean Architecture, and Domain-Driven Design. While it contains an opinionated design and structure, it was built to be extensible and flexible so you can modify and adapt it according to your team's needs and preferences.

This version of the boilerplate is still in beta, so might contain abstractions that will change or be missing features. Contribution from the community, either through PRs or is welcome.

Important This is the documentation for the v3 of the boilerplate. Click here if you want the docs for the v2.1.

Usage

How to run the server

During development, the project can be run in two different ways.

If you want to just run the application in development mode, use the following command:

$ yarn dev

To run the application in debug mode in a way that the execution will stop when a debugger statement is called, use:

$ yarn debug

How to run the application console

You can also run the application in console mode, giving you programmatic access to the environment, this can also be done in two different ways.

To run a new instance, isolated from the server, use the following command:

$ yarn cli

For the new instance, you're able to access the dependencies registered in the container using registry.<dependencyName> or through the container variable.

But if you're already running the server (this is a requirement) and you want to a console connected to the process of the server, giving you access to the current state of it, use:

$ yarn remote [server address] [REPL port]

Tests

The boilerplate is prepared to run tests using Jest. We usually group the tests in folders called __tests__ (following Jest convention) for each module of the application. To run the tests use the following command:

$ yarn test

Docker wrapper

In case you want to use Docker to run it, this project includes a docker wrapper for development. Any command can be executed by calling the scripts under the dbin/ folder.

$ dbin/yarn dev

$ dbin/yarn debug

$ dbin/yarn cli

$ dbin/yarn remote [server address] [REPL port]

$ dbin/yarn test

The container runs using host networking, so there's no need to map ports. Keep in mind that environment variables should be added to the docker-compose.yml.

Wrapper commands

# Runs the command inside an ephemeral container using the app service described in the docker-compose file as a base (use the --root flag if the command should be run as root)
$ dbin/run [--root] <command>

# Rebuild the image after any changes to the dockerfile
$ dbin/build

# Remove all containers and their volumes (WARNING any cache or files not stored in the filesystem will be deleted)
$ dbin/dispose

# Appends a RUN command to the base image, useful to install new packages
$ dbin/chimg <command>

Wrapper Aliases

# Creates a new <name> file in dbin to alias the <command> inside the docker container (use the --root flag if the command should be run as root)
$ dbin/mkalias [--root] <name> <command>

# Opens a new terminal in the project folder (use the --root flag if the shell should assume the root user)
$ dbin/shell [--root]

# Runs npm in the project folder
$ dbin/npm

# Runs npx in the project folder
$ dbin/npx

# Runs yarn in the project folder
$ dbin/yarn

Wrapper Helpers

# Adds dbin folder to the PATH only for the current terminal session.
$ source dbin/local-env

# After using this command you can use the any script inside the dbin folder without the dbin/ prefix

Dependency injection

We use Awilix to implement dependency injection and decouple the parts of your application. The boilerplate was developed in a way that each module is able to consume and augment the registered dependencies separately. Click here to know more about inversion of control and dependency injection. The creator of Awilix also has a very good series of posts about the design decisions of the library that you can read clicking here.

The instance of the Awilix dependency container is created in the file src/container.ts. You'll notice that the type of the dependencies is defined by combining the types of the dependencies exported by each of the modules. After that, each of these modules will be then responsible for registering those dependencies in the container during the boot process.

Import paths

In order to make it easier and cleaner to import files we use tsconfig-paths configured to alias the path to the src/ folder as @. For example, if you want to import the file src/article/domain/Article.ts, you can do it through the path @/article/domain/Article.ts no matter from which file you are importing it.

Modules

The codebase is divided into modules, where each module can represent either an integration (with some HTTP or database library, for example) or a feature (that are called here as app modules). Each module requires to be given a name and has access to the configuration options, the logger, the lifecycle events and the the context of the application in order to add and use dependencies from the container. More technical details about the modules will be given in a separate section.

Logging

We use Pino for effective and high performance logging. The instance of the logger is available in the dependency injection container to be used across the board and also is one of the arguments of the module constructor.

Recommended patterns

This boilerplate follows ideas from multiple good software development practices sources like layered architecture, Clean Architecture, Domain-Driven Design, among others. As such, even though it's not required, there are some patterns that are recommended and suggested that work well with the principles that make the boilerplate scalable and extensible. To mention some:

  • We recommend the usage of entities, aggregates and value objects, and other patterns that are used to isolate domain-specific code from the rest of the application code, as mentioned in Domain-Driven Design. To create a standard and practical way to define invariants for entities and aggregates there is a function that can be imported from the lib called withInvariants. Click here to read a brief reference about Domain-Driven Design.
  • To abstract the persistence layer of your application we recommend the usage of the repository pattern. An important fact to take note is that even though the example app in the boilerplate used Mongo, you're not required to use it in your application, you just need to create a module to connect to the database of your preference and implement repositories that communicate with this database
  • To favor a more predictable and explicit definition of domain rules we favor immutable objects and pure functions to define business rules rather than classes. You'll see that most of the code does not expose classes to be used.
  • To export and import domain-specific code we use TypeScript namespaces. We believe it helps in making the code that is imported from the domain easier to read since it will always be prefixed by the name of the context it concerns about, loosely inspired by Elixir modules. We follow the pattern of adding a named export called Type to expose the entity/aggregate/value object from that given file.

Going to production

To run your app in production mode, you'll need to follow these steps:

  1. Build the application with yarn build
  2. Define any environment variable important for production
  3. Start the app with yarn start

How it works

Context

The base of an application built with the boilerplate is the context instance. The context, defined in src/_lib/Context.ts and instantiated in src/context.ts, is what makes all the parts talk to each other and what defines what will be exposed to the modules during their creation, which can be customized by changing the object that is passed to the makeContext function in src/context.ts. Every module that is defined using the function makeModule provided by the same context is able to communicate with each other.

It's important to mention that you are able to have multiple isolated contexts in the same codebase in case you want to have more than one application, they will have isolated dependency containers, modules and will run separately. The context implementation will ensure that modules from different contexts are isolated, so you should not try to plug a module from a given context into a different context's bootstrap function.

Module internals

Modules are the building pieces of an application built with this boilerplate. You can either encapsulate an integration or a feature using a module or any other division you think makes sense for your application. During the boot process of the application, all the modules will be imported and run in the order they are passed to the bootstrap function in src/_boot/index.ts, this order is important because it can influence how a module will depend on another module and in the cleanup process when the app is stopped. When run, a module is able to access the configuration options of the application, the dependency injection container, and register to subsequent lifecycle events of the boot process. If some cleanup logic is needed to be run for a module during the stopping process of the application, the module must return a function that implements this logic, similar to how React implements the cleanup of effects.

Module constructors should be used mostly as simple glue between the integration or feature implementation and the application, prefer to put the actual implementation of the logic inside the _lib/ folder (like database module does with MongoProvider) or a feature folder inside src/ (like we do for the article module) accordingly.

Lib and shared kernel

Besides the feature folders that go inside src/, we also separate abstractions that will be used across the codebase in the _lib/ and _sharedKernel/ folders. Inside _lib/ we usually put code that is not specific to our application, code that could be easily extracted to a npm package if necessary, like abstractions around other packages, reusable generic types and the such. For application-specific reusable logic between multiple modules we use the _sharedKernel/ folder. To understand more about what the shared kernel is, we recommend reading the DDD quick reference section about it.

Boot

The boot process consists of setting up all the necessary code for the application to run, including running the modules and going through the lifecycle events. The files used during the boot process are all inside the _boot/ folder, including the definition of the integration modules, but the abstractions created for this, like the context, are imported from the lib. To understand more about the booting process begin looking into the src/context.ts file and then the src/_boot/index.ts file.

Lifecycle events

Both the boot and stopping processes are defined as a sequence of lifecycle events. These events exist in order to make these processes explicit and allow the modules to hook into them to properly integrate them into the application execution. Here's the order of lifecycle events for the boot and the stopping processes, we're gonna cover how to hook into an event in the next section.

Boot:

  1. Booting:
  • The booting event is dispatched once the function bootstrap is called in src/_boot/index.ts
  • The modules are invoked during this lifecycle step, so by the time each module is invoked this lifecycle event was already dispatched
  • It's not possible for a module to hook into this event because it's run by the context to declare that the boot process is started
  • Mostly for internal usage to prepare the rest of the boot process
  1. Booted:
  • When this event is dispatched it's a message to let the modules know that all the modules were already invoked and had already hooked into the other lifecycle events
  • This is a good place to register error handlers because every module has already registered its routes when they were invoked
  • Use this event to do anything you might need after all the module constructors are done running
  1. Ready:
  • This lifecycle event happens after all the listeners for the booted event were run
  • This is the proper place to actually start things, like starting the server, make queue consumers start listening to messages, or waiting for commands in case the app is running in REPL mode
  1. Running:
  • After everything from the ready event is done, the app is now actually running
  • A good usage for this lifecycle event is to know if the app is already prepared to be accessed during the setup of an automated test or offer info about the process in the console

Stopping

  1. Disposing
  • It's during this lifecycle event that the cleanup functions returned by the modules will be run
  • To make the cleanup process consistent, the cleanup functions are run in the inverse order their modules were passed to the bootstrap function. So if your app uses bootstrap(database, server), during the disposing process the cleanup function of the server module will be called first and then the database one.
  • As an example, this is where the server is stopped and the database connections are closed
  • It's intended to be used to revert everything initialized during Booting lifecycle event
  1. Disposed
  • By the time Disposed event is dispatched, we expect that everything that keeps the process open is already finished, leaving it in a safe state to be terminated
  • You could use this event to clean temporary files, for instance
  1. The application should now be completely stopped and the process is terminated

To be able to hook into lifecycle events, access the property app in the object passed to the constructor of the modules. The app object contains a function for each lifecycle, prefixing it with the word on. So, for example, to hook into the Booted event, call app.onBooted(callback).

node-api-boilerplate's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-api-boilerplate's Issues

Separate API tests from unit tests

Subdivide the test folder into two new folders, test/unit and test/features/api so the API tests won't influence the coverage of unit tests.

await till database is clean

I have a problem and is that the clean database script just returns the promise and the caller doesn't wait for it

it should look like:

beforeEach(async() => {
  await cleanDatabase();
});

I'm not using sequelizejs though, I adapted it to knex but I just checked sequelizejs documentation and it tructate returns a promise indeed.

How reliable is the code for Production ?

Firstly, Thanks for good scaffolding,
I just wanted to know How reliable is it for production usage, Is this architecture is efficient ?
I don't have experience in Large applications,
So is it good idea if I plan on using this for fairly medium-large application ?

Please help me out.

AwilixTypeError when trying to resolve a registered dependency

type : "InternalServerError"
message : "build: expected targetOrResolver to be a function or class, but got userSerializer."
stack : "AwilixTypeError: build: expected targetOrResolver to be a function or class, but got userSerializer.\n at Function.assert (c:\_dev\sgdtserver\node_modules\awilix\lib\errors.js:83:19)\n at Object.build (c:\_dev\sgdtserver\node_modules\awilix\lib\container.js:337:30)\n at middlewareFactoryHandler (c:\_dev\sgdtserver\node_modules\awilix-express\lib\invokers.js:106:34)\n at Layer.handle [as handle_request] (c:\_dev\sgdtserver\node_modules\express\lib\router\layer.js:95:5)\n at trim_prefix (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:317:13)\n at c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:284:7\n at Function.process_params (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:335:12)\n at next (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:275:10)\n at Function.handle (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:174:3)\n at router (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:47:12)\n at Layer.handle [as handle_request] (c:\_dev\sgdtserver\node_modules\express\lib\router\layer.js:95:5)\n at trim_prefix (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:317:13)\n at c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:284:7\n at Function.process_params (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:335:12)\n at next (c:\_dev\sgdtserver\node_modules\express\lib\router\index.js:275:10)\n at scopePerRequestMiddleware (c:\_dev\sgdtserver\node_modules\awilix-express\lib\scope-per-request.js:13:16)"

An folder structure example needed

Hey !

I want a little help in understanding your folder and dependency injection structure :

If I am trying to implement an external Emailing WebAPI (SendGrid), how do I organize the files along your file structure ?

sendGrid basically has a library and I need to implement a wrapper around it. How do I do that.
my solution so far is to :

  • create a database model (necessary)
  • create a sendGridFolder under interfaces where I add the sendGridWrapper
  • create an Email Folder under infra then under it create SendGrid folder and within add a new Repository sendGridRepository that must have the sendgridWrapper injected to it via the constructor as well as the Database model.
  • create a domain model named Email
  • create an operation folder under app called Email and add email operations under it ( sending, checking status, .... ) this will use the domain model

Is this the correct way of using your approach ?
if else can you please specify, in a flowchart type fashion, the steps that a basic db query coming from and API call to this system would take ? ans maybe the files that it goes through ? something like this file will call this file and the use the dependency injected right here, .....

Thanks, you work is very good but I found it hard to understand with ease. so if you can spare some time to help me, this would be very nice.

Injecting Domain models

Great project! I literally study the code.

Question: Why not inject your domain entities via awilix? UserRepository grabs the User entity via require()

Fastify working example

The current version of the boilerplate uses Express for the HTTP layer, it would be good to have a working example using Fastify as well. Ideally, it should not affect any layer other than the HTTP layer itself and it could live in a separate branch and release so people can see both of them.

Time field cannot be queried.

I have tried several methods, is it related to the version of the database?
Exists in the database, but in the code is the query does not come out.

use Heroku database in development

I'm trying to initiate a project with Heroku database in development mode.

I updated config/database.js:

  development: {
    username: 'XXXX',
    password: 'XXXXX',
    database: 'XXXXX',
    host: 'XXXXX.compute-1.amazonaws.com',
    dialect: 'postgres'
  },

Loaded configuration file "config/database.js".
Using environment "development".

ERROR: connect ECONNREFUSED

What I'm doing wrong?

Middleware before API routes

Hey man! Really love your work on this!

How'd you go about using middleware before specific API routes? Would you inject them as dependencies into the Controller or just require them?

throw new errors_1.AwilixTypeError('asClass', 'Type', 'class', Type);

H:\node-api-boilerplate-master\node_modules\awilix\lib\resolvers.js:77
throw new errors_1.AwilixTypeError('asClass', 'Type', 'class', Type);
^

AwilixTypeError: asClass: expected Type to be class, but got undefined.
at asClass (H:\node-api-boilerplate-master\node_modules\awilix\lib\resolvers.js:77:15)
at Object. (H:\node-api-boilerplate-master\src\container.js:81:15)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Module.require (internal/modules/cjs/loader.js:1044:19)
at require (internal/modules/cjs/helpers.js:77:18)
at Object. (H:\node-api-boilerplate-master\index.js:1:19)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
at internal/main/run_main_module.js:18:47
[nodemon] app crashed - waiting for file changes before starting...

Different input validations for different actions

Hi. Usually we need different schema validations for different actions on same entity. For example, we don't want to update some user's fields but we need them when creating new user. But we have just one schema for user in domain layer. Is it legal to create multiple schemas for create, update and other operations for one entity in domain layer?

Trying to install dependencies using npm throws error

I'm trying to install dependencies using the node package manager (npm) but it throws an error in the middle of installation and stops further installation. I went through this quick start it says to install dependencies using yarn package manager but I thought of installing it via npm!

This is the snap of the error I got,
image

Node version is 10.16.3
NPM version is 6.9.0
OS Ubuntu 18.04.3 LTS

It could be great if we have both npm and yarn package manager support

Elegant way of implementing listeners

I'm using this boilerplate, and I like it a lot.

But I'm struggling with a elegant way of creating listeners.
This is what I want to do:

Have a Operation in the Application layer that emits an event.
Have a separate Listener in the Application layer (I think that's the best layer for it, but that's debatable) that listens to the event and does something on Succes.

Currently I register the Operation and the Listener through the DI container.
The Listener get's the instantiated as a function, and gets the instantiated Operation (a singleton) through DI, and it attaches itself as a listener.
But this Listener function has to be executed for the attachment to take place. So in code:

// NewOperation.js
class NewOperation extends Operation {
constructor({ logger }) {
    super();
    // constructor code
  }

  async execute() {
    const { SUCCESS, ERROR } = this.outputs;

    // code
  }
}

NewOperation.setOutputs(['SUCCESS', 'ERROR']);

module.exports = NewOperation;
// Listener.js
module.exports = ({ newOperation, logger }) => {
  const { SUCCESS, ERROR } = newOperation.outputs;
  
  newOperation
    .on(SUCCESS, (payload) => {
      logger.info(payload);
    })
    .on(ERROR, (error) => {
      logger.info(error);
    });
};
// container
newOperation: asClass(NewOperation, { lifetime: Lifetime.SINGLETON }),
listener: asFunction(Listener, { lifetime: Lifetime.SINGLETON })
// Somewhere, currently I do this in the entry point index.js
container.resolve('listener');

The above code works, but it feels a bit convoluted and ugly. Is there a recommended way for doing this?

How to deal with association many to many ?

Hi,
With my mate, we are junior developper and we try to refactor our system with your boilerplate ! I have just a problem during my learning process, i had try to do a new Model Team (no problem for this) and now i try to do a n-n relation with pivot table TeamsUsers. I haven't find any example of this and i don't see how to make it properly.
Can i get a simple example or some link with info about this ?
Hope my sentences are clear !

GraphQL working example

A working example of a GraphQL API would be welcoming. It should follow the same integration modules approach we have for other integrations and be pluggable in a way that opting out of it be a matter of removing from the bootstrap() call.

Implement authentication with JWT

Since JWT is the most used authentication method for APIs nowadays, it would be good to have an implementation of JWT for the boilerplate. It should be reusable across multiple HTTP abstractions (Express, Fastify, ...) with a thin integration layer to plug into a given HTTP abstraction.

UnhandledPromiseRejectionWarning: TypeError: Object.defineProperty called on non-object

@nfroidure
i am getting this type of error when i try to call create todo api

i am adding todo and todoitems in this boilerplate

error is as below

"(node:7788) UnhandledPromiseRejectionWarning: TypeError: Object.defineProperty called on non-object
at defineProperty ()
at new decorator (H:\node_boilerplate\node_modules\structure\src\attributes\decorator.js:45:5)
at CreateTodo.execute (H:\node_boilerplate\src\app\todo\CreateTodo.js:13:18)
at create (H:\node_boilerplate\src\interfaces\http\todo\TodosController.js:33:16)
at Layer.handle [as handle_request] (H:\node_boilerplate\node_modules\express\lib\router\layer.js:95:5)
at next (H:\node_boilerplate\node_modules\express\lib\router\route.js:137:13)
at injectMiddleware (H:\node_boilerplate\node_modules\awilix-express\lib\inject.js:11:12)
at Layer.handle [as handle_request] (H:\node_boilerplate\node_modules\express\lib\router\layer.js:95:5)
at next (H:\node_boilerplate\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (H:\node_boilerplate\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (H:\node_boilerplate\node_modules\express\lib\router\layer.js:95:5)
at H:\node_boilerplate\node_modules\express\lib\router\index.js:281:22
at Function.process_params (H:\node_boilerplate\node_modules\express\lib\router\index.js:335:12)
at next (H:\node_boilerplate\node_modules\express\lib\router\index.js:275:10)
at injectMiddleware (H:\node_boilerplate\node_modules\awilix-express\lib\inject.js:11:12)
at Layer.handle [as handle_request] (H:\node_boilerplate\node_modules\express\lib\router\layer.js:95:5)
(node:7788) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block,
or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:7788) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code."

this is my repository with all changes i have done

nodemon restart loop from file changes

Expected behaviour
being able to access http://localhost:3000/api/users via npm run dev

Actual behaviour
nodemon] 1.11.0
[nodemon] to restart at any time, enter rs
[nodemon] ignoring: /Users/cuyler/node-api-boilerplate/.git//* .nyc_output .sass-cache bower_components coverage /Users/cuyler/node-api-boilerplate/node_modules//*
[nodemon] watching: .
[nodemon] watching extensions: js,json
[nodemon] starting node index.js
[nodemon] child pid: 27870
[nodemon] watching 72 files
[nodemon] files triggering change check: src/interfaces/http/utils/createControllerRoutes.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/utils/createControllerRoutes.js

[nodemon] files triggering change check: src/interfaces/http/logging/loggerMiddleware.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/logging/loggerMiddleware.js

[nodemon] files triggering change check: src/interfaces/http/swagger/swaggerMiddleware.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/swagger/swaggerMiddleware.js

[nodemon] files triggering change check: src/interfaces/http/swagger/swagger.json
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/swagger/swagger.json

[nodemon] starting node index.js
[nodemon] child pid: 27878
[nodemon] files triggering change check: src/interfaces/http/user/UserSerializer.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/user/UserSerializer.js

[nodemon] starting node index.js
[nodemon] child pid: 27882
[nodemon] files triggering change check: src/interfaces/http/router.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/router.js

[nodemon] starting node index.js
[nodemon] child pid: 27885
[nodemon] files triggering change check: src/interfaces/http/user/UserSerializer.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/user/UserSerializer.js

[nodemon] starting node index.js
[nodemon] child pid: 27889
[nodemon] files triggering change check: src/interfaces/http/router.js
[nodemon] matched rule: **/.
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] src/interfaces/http/router.js

...

this loop continues between src/interfaces/http/user/UserSerializer.js and src/interfaces/http/router.js

to get around I added this to the dev in package.json

"dev": "cross-env NODE_PATH=. NODE_ENV=development nodemon --ignore 'src/interfaces/http/*.js' --verbose"

after restarting from here I was able to hit the api endpoint

Promises vs EventEmitter

Hi @talyssonoc,
really great boilerplate!

I've got a question, where I'm struggling to get a good justification to use EventEmitter rather than Promises in response to the execute method in every Operation...

The only advantage that I see so far with the EventEmitter approach is the ability to subscribe the operation in different part of the application...
It should not be an easiest approach to return a promise from the execute method where the success of the promise would return the result and the failure would return the error?
This would also reduce the memory leak produced by the EventEmitter.

how would a Mapper support relationships?

Hi,
When you have simple data-values objects such as your User, a Mapper is pretty simple as it just maps the properties between DB model and domain.

but how would you deal with relationships?
for example, if a User can potentially have a "ReferrerUser".

now the mapper should support both user with and without a referrer user.
the problem is "lazy loading". sometimes you want just the Id of the referrer, and sometimes the actual user object, depending on the use-case.

examples for different scenarios:

  1. create a user without referrer
  2. create a user + create its user referrer at the same time
  3. create a user with user id of the user referer (it already exists in the system)
  4. fetch user with just its referer id if exists
  5. fetch user + eagerly fetch the referrer user as a nested object

also, what if I now also want the user to have a collection of all the users he referred? I would add "referredUsers" array to the domain
the problem is that sometimes I would want to fetch just the user, and sometimes I would want to fetch the user + his referred users (eager loading). and so on ..

moreover, in this example, the relationship is to the same type. what if the relationship is to another type? for example, if User has a relationship to "Country". now I would also need to delegate a call to CountryMapper inside UserMapper?

lastly, when there is no 1-to-1 connection between the domain and the table persisting it, mapper might impose more difficulties to "glue" things together

hope my concerns are clear

Why do not move serialization function to domain model class?

I mean if User class would have method to serialize itself then you don't need to have special UserSerializer function separately and serialization setup in that case will be more transparent (because it will be made in the same place as model definition). So you can define public and protected properties of model and serialize only public properties for example

Dev Environment not starting up

hi, I cloned the repository and followed all processes as described on the readme to run the development server. However, the logged output is stuck on the following lines in the console;

cross-env NODE_PATH=. NODE_ENV=development nodemon
[nodemon] 1.11.0
[nodemon] to restart at any time, enter rs
[nodemon] watching: .
[nodemon] starting node index.js
[nodemon] clean exit - waiting for changes before restart

what am I doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.