简体   繁体   中英

Domain Driven Design for Node.js

I'm currently in the process of architecting a Node.js microservice-based application, and I'd like to make use of Domain Driven Design as a guideline for structuring the individual services. I have a few questions on this as follows:

  1. As far as I understand, the domain layer should typically contain repository interfaces, and specific implementations of these interfaces should then be created in your infrastructure layer. This abstracts the underlying storage/technical concerns out of your domain layer. In the context of my project, seeing as JavaScript does not natively support things like interfaces, how would one go about achieving a similar effect?
  2. One service in particular will be handling authentication by means of OAuth. Would one typically classify OAuth-related logic as an application service? Or would it fall under the infrastructure layer? My understanding is that it's not infrastructure-related, nor is it related to the core domain, but is still required as part of serving the application to clients. Hence, I've placed it within the application layer for now.
  3. Following on from point 2, where would OAuth-related entities/repositories (ie tokens/clients) best be placed? I'm tempted to keep them in the domain layer along with my other entities/repositories, even though they aren't technically a business concern.

Some notes to add to the above:

  1. I'm not keen on using TypeScript (as suggested here ).
  2. I am fully aware that some may consider JavaScript as being non-suitable for DDD-type approaches. I'm familiar with some of the pitfalls, and once again, I'm using DDD as a guideline.

On a side note, I have come up with the following project structure, which was heavily inspired by this great example of a DDD-based project in Laravel:

app/
----app.js
----package.json
----lib/
--------app/
------------service/
----------------oauth.js
----------------todo.js
--------domain/
------------list/
----------------model.js
----------------repository.js
----------------service.js
------------task/
----------------model.js
----------------repository.js
----------------service.php
--------http/
------------controller/
----------------list.js
----------------task.js
------------middleware/
----------------auth.js
----------------error.js
--------infrastructure/
------------db.js
------------logger.js
------------email.js

I would love to hear any thoughts you may have on this layout. I'm fully aware that the topic of project structure is somewhat opinion-based, but I'm always keen to hear what others have to say.

Have you considered wolkenkit ?

It's a CQRS and event-sourcing framework for Node.js and JavaScript, that works pretty well with domain-driven design (DDD). It might give you a good idea of how to structure your code, and also a runtime to execute things without having to re-invent the wheel.

I know the guys behind it and they invested 3-4 years of thoughts, blood and sweat into this.

Domain-Driven Design guides decomposition of a system into a set of bounded contexts/services/microservices. However, the way you design each service is individual and depends on the service's business domain. For example, your business's core domain services and supporting domain services should be architected in different ways.

Even if this question is quite old, I think it will be useful to add a precision on your first interrogation :

As far as I understand, the domain layer should typically contain repository interfaces, and specific implementations of these interfaces should then be created in your infrastructure layer. This abstracts the underlying storage/technical concerns out of your domain layer. In the context of my project, seeing as JavaScript does not natively support things like interfaces, how would one go about achieving a similar effect?

JavaScript does not provide interface. Instead of mimicking OOP concepts, why not have a look to more functional ones? Higher-order functions are very well suited for javascript, you can use them to "declare" dependencies and inject them at runtime :

const { FooData } = require('./data');

const getFooOfId = (getFooOfIdImpl = async (fooId) => { throw new Error(`Can't retrieved foo of id ${fooId} : missing implementation`) }) => async fooId => {
  try {
    const fooData = await getFooOfIdImpl(fooId);
    return FooData(fooData);
  } catch (err) {
    throw new Error(`Unable to retrieve Foo with id ${fooId}`);
  }
}

/*
This function is used to build a concrete implementation, for example with an in-memory database :
const inMemoryFooDatabase = {
  foo17: {
    id: 'foo17',
    foo: 'a foo value',
    bar: 'a bar value',
    foobaz: 1234,
  },
};
const getFooOfIdFromInMemoryDatabase = getFooOfId(fooId => inMemoryFooDatabase[fooId])
*/

module.exports = {
  getFooOdId,
}

Nothing prevents you to bypass totally this function because there is no strong type-checking in javascript, but it acts as a declaration of your domain "interfaces" needs.

If you want to learn more about this, you can read my post on this subject : Domain-Driven Design for JavaScript developers

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM