Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Domain Driven Design in Node.js Application

TL; DR; I'm looking for trite example of DDD node.js application.


Hi,

I'm going to create node application. I wonder that I can not find any example of application with business logic separated in domain.

OK, there are some examples like: https://github.com/adrai/node-cqrs-domain - but this is whole CQRS with event sourcing implementation.

My idea is to do that like that:

//domain/book.js
function Book(title, author)
{
  this._title = title;
  this._author = author;
}

// domain methods ...

//infrastructure/persistance/repository/book-repository.js
function BookRepository()
{}

BookRepository.prototype.save(book)
{
  var bookModel = mappers.mapToOrm(book);
  return bookModel.save();
}

// [...] get, getAll, getNextId

//infrastructure/persistance/orm/book.js
//using http://bookshelfjs.org/
var Book = bookshelf.Model.extend({
  tableName: 'books'
});

//infrastructure/mappers/book-mapper.js
function mapToOrm(book) {
  //mapping [...]
  return new persistance.Book();
}

function mapToDomain(domain) {
  //mapping [...]
  return new domain.Book();
}

but on the other hand I've never seen any similar solution (with domain model, orm model, repository and mappers). Am I thinking in the right way? Maybe there is no reason to separate business logic in domain in node.js applications. If so, why? If not, can you send me an example of DDD implementation or improve my code?

[2017/01/13]

I've created sample application in TypeScript. For now without repositories and not much services. Issues and pull requests are welcome. https://github.com/dawiddominiak/ddd-typescript-bin-packing-problem-solution

like image 981
Dawid Dominiak Avatar asked Dec 01 '15 18:12

Dawid Dominiak


People also ask

What is Domain-Driven Design example?

An aggregate is a domain-driven design pattern. It's a cluster of domain objects (e.g. entity, value object), treated as one single unit. A car is a good example. It consists of wheels, lights and an engine.

What is meant by Domain-Driven Design?

Domain-driven design (DDD) is a software development philosophy centered around the domain, or sphere of knowledge, of those that use it. The approach enables the development of software that is focused on the complex requirements of those that need it and doesn't waste effort on anything unneeded.

Where is Domain-Driven Design used?

Domain-driven design is perfect for applications that have complex business logic. However, it might not be the best solution for applications with minor domain complexity but high technical complexity. Applications with great technical complexity can be very challenging for business-oriented domain experts.

What is Domain-Driven Design in microservices example?

Initially coined by Eric Evans, Domain-Driven Design (DDD) is defined as designing software systems based on the underlying model of the business domain. Domain-Driven Design has an organizational relationship to microservices and categorizes them so their usage is easily understood.


2 Answers

I'm very new to Node.js world.

But I believe if you do your work using TypeScript with Node you can force most of DDD principles to be used.

The problem "and advantage in the same time" in node.js that there aren't so restrictions like we have in OOP languages like C# or Java. and this freedom "and messy" of JavaScript making create robust complex DomainModel and Business logic very hard

like image 131
Wahid Bitar Avatar answered Oct 11 '22 12:10

Wahid Bitar


I'm looking to do the same thing at the moment, and I'm coming from the Ruby world. So, let me do 2 things:

  1. Point you to the best Ruby implementation I've seen of Domain-Driven Design I have found, Hanami: http://hanamirb.org/guides/models/overview/ which you could use as a reference.

  2. Discuss what I'm finding (literally right now, as I type) to attempt to find the analogs in Node.

I've found this page: https://github.com/sindresorhus/awesome-nodejs

which catalogs a ton of high-quality / high-popularity Node packages.

The first thing is, we need something that is going to do validation and schema construction for our Domain Models. Just looking at the first entry in the data validation section, Joi seems to be a decent choice for that:

https://github.com/hapijs/joi

For Persistence of the domain objects, I'm just setting up a stub object, borrowing from Hanami's interface:

var repo = {
  find: function(entity_name, id) {
    //  - Fetch an entity from the collection by its ID
  },
  create: function(entity_name, data) {
    //  – Create a record for the given data and return an entity
  },
  update: function(entity_name, id, data) {
    //  – Update the record corresponding to the id and return the updated entity
  },
  delete: function(entity_name, id) {
    //  – Delete the record corresponding to the given entity
  },
  all: function(entity_name) {
    //  - Fetch all the entities from the collection
  },
  query: function(entity_name, query_object) {

  },
  first: function(entity_name) {
    //  - Fetch the first entity from the collection
  },
  last: function(entity_name) {
    //  - Fetch the last entity from the collection
  },
  clear: function(entity_name) {
    //  - Delete all the records from the collection
  }
}

module.exports = repo

whether you choose to use Bookshelf, Sequelize, or even the LoopBack framework, you can code an object that is going to fit the above interface that then does the dirty work of integrating with those frameworks.

If I were to try different ORM's, I would create a different repo object for each of the above. Notice that as I've written it, the repo is a singleton that is aware of different entities and how to persist them. In many cases, this will no doubt delegate to different repository objects on a per-entity basis. However, that might not always be true. a simple in-memory repo, could just have an array of objects for each entity.

That leaves Services/Interactors - The functions/classes that actually do work. These are easy - they are the ones that take a domain object, perform some business logic, and in the CRUD cases, make a call out to the Repository. A probably-syntactically-wrong example:

const repository = require('./myFileRepository')

function createBook(bookEntity) { 

  if(bookEntity.valid?) { 
    repository.create('book', bookEntity)
    return true
  }
  else {
    return { error: 'book not valid' }
  }
}

module.exports = createBook

for the Service functions, I just today learned about Node Machines, and they seem like a really smart idea: http://node-machine.org

they seem to be a JS-attempt at monads + documentation. so I'm considering writing them up like that.

anyway, given it's been a year since your post, you've probably moved on. hope this helps you / the community!

like image 32
MissingHandle Avatar answered Oct 11 '22 12:10

MissingHandle