Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DDD approach in JHipster #11122

Closed
hdurix opened this issue Jan 14, 2020 · 59 comments
Closed

DDD approach in JHipster #11122

hdurix opened this issue Jan 14, 2020 · 59 comments

Comments

@hdurix
Copy link
Member

hdurix commented Jan 14, 2020

DDD approach

For now, JHipster is only able to create CRUD entities and JHipster's goal is to help developers in building all the technical aspect of an application.
It could be interesting to explore how JHipster could also help them build the "business" of the application.
One approach for that is the methodology called Domain Driven Design.

Why this ticket?

This ticket is to:

  • record what is being done to explore a DDD approach in JHipster
  • gather ideas from the community for turning a JHipster application in a more DDD-compliant one

First step

I would like to work on a application generated by JHipster and to refactor it in order to bring DDD concepts (aggregates, entities, value objects, domain events, ...) and have example to show and to discuss about.

More to come...

If some of you have experience with bringing DDD concepts to a JHipster application, feel free to participate, to give ideas and feedback.

@pascalgrimaud
Copy link
Member

For me, it is a must have. It can be helped by repositories as example, by documentation, by modifying the current generated code etc...
There is a lot of work I think, so I'm putting a big bounty on this. You'll be the lead on this part @hdurix, don't hesitate to ask help

@pascalgrimaud pascalgrimaud added $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ $500 https://www.jhipster.tech/bug-bounties/ labels Jan 14, 2020
@pascalgrimaud
Copy link
Member

cc @nonomoho as you probably have ideas on this

@johnbanq
Copy link

I think the most important part of DDD lies in the strategic patterns(bounded contexts and context mapping) instead of the tatics patterns(repositories, aggregates etc). As long as you have a clear separation of contexts, a good idea of their relationships and a conceptual model within each context, you are using DDD even you are just CRUDing all the way through in each these contexts.

So perhaps we should start by telling people how to organize their code by contexts and apply “clean architecture“ within some key context which have a relatively complex domain model, instead of going for the tactics patterns right away?

@huytmb
Copy link
Contributor

huytmb commented Jan 15, 2020

I completely agree with @johnbanq advice. In early phase, starting with "clean architecture" for complex domain model.

@gmarziou
Copy link
Contributor

Do you have a specific flavor of "clean architecture" in mind? Hexagonal, onion, ...
Personally I like the modulith approach: package by feature/context and using spring events for inter module communication

@mohamedzaki90
Copy link
Contributor

@gmarziou spring events have the problem of lost events if an exception happens during event publication, listeners will never get notified

@gmarziou
Copy link
Contributor

gmarziou commented Jan 15, 2020

@mohamedzaki90 Well, I never experienced this in my apps, do you have some reference to share?

My understanding is that if an exception is thrown during event publication, the publisher will get it and its transaction will rollback. So I can't imagine how you could lose an event at least in a transactional context. If you' re not in a transaction, then you will have to catch and manage the exception.

@mohamedzaki90
Copy link
Contributor

@gmarziou take a look at the caveats here
https://www.baeldung.com/spring-data-ddd

here's a proposed workaround (by the author of @modulith)
https://github.com/odrotbohm/spring-domain-events

@gmarziou
Copy link
Contributor

gmarziou commented Jan 15, 2020

Thanks, I have never experienced this case maybe because I always used them inside a transaction and recording state change in database both from publisher and listeners, and then scheduling retry for unprocessed changes.

@gmarziou
Copy link
Contributor

Interesting DSL: https://www.innoq.com/en/blog/code-your-model/

@johnbanq
Copy link

johnbanq commented Jan 16, 2020

Do you have a specific flavor of "clean architecture" in mind? Hexagonal, onion, ...
Personally I like the modulith approach: package by feature/context and using spring events for inter module communication

My take on this is pretty much the same, build modulith and use a hexagon-ish architecture in each of the contexts but I also allow cross-context communication by calling interfaces in a dedicated "api" package in each context(the implementation of the interfaces are provided by DI).

Maybe we can build a small sample that demonstrates this as a starting point?
PetClinic might be a good project to start

@ksilz
Copy link

ksilz commented Jan 20, 2020

@hdurix I hope to start on my first DDD JHipster / Angular application on Feb 1. To me, DDD has two advantages:

  • Makes it easier to change application in the future: That’s the obvious advantage.
  • Indirectly enables JHipster updates: I think one of the woefully underused benefits of JHipster is to keep your application up-to-date. In order to do that efficiently, you need to stay away from the generated code as much as possible. DDD forces you to do that, I think, because your whole application is separate from the generated code. I still plan to use the generated back-end code (services, repositories), but ideally unchanged. The less you change the generated code, the less the pain from JHipster upgrades.

@phipex
Copy link

phipex commented Feb 6, 2020

It seems to me that a functionality associated with this tickect could be to create a multi-module project with the same notation in JDL of microservices, that is to say, use the same microservice notation to map DDD modules, said roughly, the possibility of having microservices packaged in the same application.

@pascalgrimaud
Copy link
Member

@hdurix : what is the state of this ticket ?

@avdev4j
Copy link
Contributor

avdev4j commented Jun 30, 2020

hi @hdurix, do we have to consider this ticket for the v7?

@avdev4j avdev4j added this to To Do in v7 Jul 1, 2020
@ksilz
Copy link

ksilz commented Jul 1, 2020

@hdurix After close to 5 months of using DDD with JHipster, I can wholeheartedly recommend this approach:

  • My business logic is tucked away neatly, using the JHipster back-end classes.
  • I created my own front-end, independent of the generated CRUD screens.
  • Updating the entities is a breeze. I created a shell script to fix the JHipster generated code: Adding a standard column set to all entities tables, putting in base classes for DTOs & entities, adding my own interface with query methods to the JHipster repository interface.
  • And the last two JHipster upgrades were pretty quick, too. I'll probably do the JHipster 6.10 one next week.

@ecostanzi
Copy link
Contributor

@ksilz that's interesting. Are you willing to share more details about the project structure? As @johnbanq suggested, we should start with some samples.

@desprez
Copy link

desprez commented Jul 3, 2020

DDD strategic patterns are just a way of naming, delineating and organizing concepts.
I think developers who want to use a DDD approach in their project want to be able to use tactical DDD patterns.
In my opinion, the biggest challenge with the DDD approach in JHIPSTER is to model the processing and the business rules inside the entities which will be overwritten during the next regeneration. The “side by side” approach does not work well in this case without making the domain model more complex, which we especially don't want in DDD.
Another thing, it is also currently impossible to model concepts in JDL using ValueObjects.
It can be a good start ...

@ksilz
Copy link

ksilz commented Jul 5, 2020

@ksilz that's interesting. Are you willing to share more details about the project structure?

@ecostanzi Sure! In my current projects, I have three goals:

  1. Keep doing JHipster upgrades: Since the upgrade regenerates all files, I keep my changes as much away from "JHipster files" as possible. For instance, I have my own Gradle build file that the main build file includes. I don't add query methods to the Spring JPA repository files; instead, they extend my own interface where I put my methods. And I have my own set of Docker files.
  2. Use and "extend" JDL: I have both my application and my entities defined in JDL files. I don't touch the JHipster generated files anymore but update the entity JDLs so that they accurately reflect my data model. But I do have some columns/fields that all my entities share. So through a shell script, I make all the required changes, like adding database columns to the Liquibase DB scripts and setting base classes for my entity classes and DTOs.
  3. Use DDD & clean architecture: This is my first DDD project with a clean architecture. I have my own, handwritten Angular UI und my own Java backend. But I use the JHipster back-end code, either starting that the service method level (with DTOs) or at the JPA query level, as my "persistence layer". I currently have four different bounded domains. In my code, there's a "clean" package/folder where all my code resides. That's a standard clean architecture layout in Java: The application/port/in folder has the use case, application/port/out the ports, and application the services that implement the use case with the help of the ports. adapter/out has the adapters implementing my ports, and adapter/in the REST controllers. My domain classes are in domain. Angular is nearly the same: It's missing the application/port/in folder but has a ui folder with Angular UI modules. I still have JHipster generate an Angular UI, but I disabled its loading and move all these UI files into a separate folder outside of src (just in case I want to see how something is done there in the latest JHipster version).

@pascalgrimaud pascalgrimaud removed $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ $500 https://www.jhipster.tech/bug-bounties/ labels Sep 26, 2020
@pascalgrimaud pascalgrimaud moved this from To Do to In progress in v7 Sep 26, 2020
@deepu105
Copy link
Member

deepu105 commented Sep 26, 2020

@pascalgrimaud if you think thats appropriate its ok for me. Anyway my aim was just to disable direct commit to master without PR. The review rule was just a way to achieve it

@deepu105
Copy link
Member

deepu105 commented Sep 26, 2020 via email

@DevAlves1993
Copy link

DevAlves1993 commented Sep 26, 2020

Hello,

I would like to propose the following hierarchy for the domain package and application package, the objective being to have a hierarchy quite close to the principles defined by the Domain Driven Design and Hexagonal Architecture.

Domain Layer

├───domain/
│   ├───entity/
│   ├───objectvalue/
│   ├───exception/
│   ├───service/

SUB-DIRECTORY CONTENT REQUIRED
Entity The business entities of the domain. Yes
ValueObject The business value objects of the domain. No
Exception Business exceptions in the domain. No
Service Business Domain Service. No

Application Layer

├───application/
│   ├───usecase/
│   ├───command/
│   ├───commandhandler/
│   ├───queries/
│   ├───port/
SUB-DIRECTORY CONTENT REQUIRED
UseCase The use cases are the processes that can be triggered in our Application Core by one or several User Interfaces in our application. Yes
Command The commands used in use cases supported by the command handler. Yes
CommandHandler The Command Handlers. Yes
Queries Query Managers or Handlers. No
Port Secondary ports. No
DTO Data transfert object No

The Command Handlers can be used in two different ways:

  • They can contain the actual logic to perform the use case;
  • They can be used as mere wiring pieces in our architecture, receiving a Command and simply triggering logic that exists in an Application Service.

The secondary ports will be interfaces implemented in the infrastructure.
Typically, their role is to :

  • Use a repository to find one or several entities;
  • Tell those entities to do some domain logic;
  • Use the repository to persist the entities again, effectively saving the data changes.

@pascalgrimaud
Copy link
Member

@deepu105 : I'm not sure we are closer to the release. There are still some work to achieve. Then, we should not target a specific date, it will be released when it's ready.
Major version is perfect to introduce breaking changes. We should not be afraid about that.

Otherwise, we'll need to wait V8, it will be in 2 years

Then, this ticket is opened since January so the users are already aware of that

@DamnClin
Copy link

@DevAlves1993 I strongly advise against this as this is totally the opposite of DDD principles. In DDD modules (packages in Java) are parts of the Ubiquituous Language. Packaging by feature allow really better designs, one of the main goal of this package organization is to keep an high cohesion in each module.

We don't really need to see immediately if some class is an Aggregate or a Value Object since it will be obvious when looking at the APIs. We do really need to know what business logic stands in a given module.

Another important thing: ports, queries and command are part of the domain otherwise you'll have to break the: "Everything depends on the domain, the domain depends on nothing" principle. The application layer must remain really thin: for a given context there is no need to split it.

@mshima
Copy link
Member

mshima commented Sep 26, 2020

@pascalgrimaud the changes I will implement are opt-in, so traditional generation will be applied if no domain is specified.
About your hexagonal project, I think we shouldn't convert the main application/user management into domain for v7 by default but it would be ok with opt-in.
But it's really cool to test different package structure.

@pascalgrimaud
Copy link
Member

@mshima : I'm beginner in this kind of architecture, don't rely on my project, I only played this morning with JHipster, and did some refactoring/renaming folder, nothing more

If we can keep both type of generation, I afraid it will be a lot of stuff to maintain...

Let's keep on thinking and discussing about how to deal with the future of JHipster

cc @jhipster/developers : your opinion is important here. Should we change the JHipster folder to match with an Hexagonal Architecture or is it too early ?

@DevAlves1993
Copy link

DevAlves1993 commented Sep 26, 2020

@DamnClin

Another important thing: ports, queries and command are part of the domain otherwise you'll have to break the: "Everything depends on the domain, the domain depends on nothing" principle. The application layer must remain really thin: for a given context there is no need to split it.

I agree with you ports, queries and command are part of the domain. The mention "Application Layer" can be confusing, I would have said "Application Business Rule".
I define the domain as the combination of the Application Business Rule and the Enterprise Business Rule and the fact that the Application Business Rule is closely related to the Enterprise Business Rule.

Packaging by feature allow really better designs, one of the main goal of this package organization is to keep an high cohesion in each module.

Indeed, an organization by functionality would be the best and would be closer to the representation of the company. What is your definition of the word "feature", is it a sub-domain or a delimited context or both?

Personal opinion :
Enterprise Business Rule and Application Business Rule must be as pure as possible, it must not depend on external elements (framework & co). Nevertheless, Application Business Rule can depend on hundreds of libraries (also quite pure) such as the Javax Validation library.

New proposals

First proposal

├───domain/
│   ├───subdomain1/
│       ├───entity/
│       ├───objectvalue/
│       ├───exception/
│       ├───service/
│       ├───command/
│       ├───commandhandler/
│       ├───queries/
│       ├───port/
│  ├───subdomain2/
│       ├───entity/
│       ├───objectvalue/
│       ├───exception/
│       ├───service/
│       ├───command/
│       ├───commandhandler/
│       ├───queries/
│       ├───port/
├───application/
│   ├───usecase/
├───infrastructure/
│   ├───primary/
│      ├───rest/
│      ├───cli/
│   ├───secondary/
│      ├───repository/
│      ├───entity/
│      ├───service/
│      ├───config/
│      ├───otherspackage/

or

├───domain/
│   ├───subdomain1/
│       ├───.... (java files)/
│  ├───subdomain2/
│       ├───.... (java files)/
├───application/
│   ├───usecase/
├───infrastructure/
│   ├───primary/
│      ├───rest/
│      ├───cli/
│   ├───secondary/
│      ├───repository/
│      ├───entity/
│      ├───service/
│      ├───config/
│      ├───otherspackage/

Second proposal

├───domain/
│   ├───application/
│       ├───usecase/
│   ├───subdomain1/
│       ├───entity/
│       ├───objectvalue/
│       ├───exception/
│       ├───service/
│       ├───command/
│       ├───commandhandler/
│       ├───queries/
│       ├───port/
│   ├───subdomain2/
│       ├───entity/
│       ├───objectvalue/
│       ├───exception/
│       ├───service/
│       ├───command/
│       ├───commandhandler/
│       ├───queries/
│       ├───port/
├───infrastructure/
│   ├───primary/
│       ├───rest/
│       ├───cli/
│   ├───secondary/
│       ├───repository/
│       ├───entity/
│       ├───service/
│       ├───config/
│       ├───otherspackage/

or

├───domain/
│   ├───application/
│       ├───usecase/
│  ├───subdomain1/
│       ├───.... (java files)/
│  ├───subdomain2/
│       ├───.... (java files)/
├───infrastructure/
│   ├───primary/
│      ├───rest/
│      ├───cli/
│   ├───secondary/
│      ├───repository/
│      ├───entity/
│      ├───service/
│      ├───config/
│      ├───otherspackage/

Third proposal

├───domain/
│   ├───subdomain1/
│       ├───application(Application Business Rule)/
│            ├───usecase/
│            ├───command/
│            ├───commandhandler/
│            ├───queries/
│            ├───port/
│      ├───core(Enterprise Business Rule)/
│           ├───entity/
│           ├───objectvalue/
│           ├───exception/
│           ├───service/
│   ├───subdomain2/
│       ├───application(Application Business Rule)/
│            ├───usecase/
│            ├───command/
│            ├───commandhandler/
│            ├───queries/
│            ├───port/
│       ├───core(Enterprise Business Rule)/
│            ├───entity/
│            ├───objectvalue/
│            ├───exception/
│            ├───service/
├───infrastructure/
│   ├───primary/
│      ├───rest/
│      ├───cli/
│   ├───secondary/
│      ├───repository/
│      ├───entity/
│      ├───service/
│      ├───config/
│      ├───otherspackage/

Personally I prefer the third proposal.

@DamnClin what would be the best hierarchy for you ?

@deepu105
Copy link
Member

deepu105 commented Sep 26, 2020 via email

@mshima
Copy link
Member

mshima commented Sep 26, 2020

@DevAlves1993 your proposal seems too specific and complex.
Having a common infrastructure for the entire application hurts DDD concepts, by hurting domain isolation.

A jdl concept:
store_domain.jdl:

entity Product {}
entity Consumer {}
entity Order {}

domain Store {
    entity Product, Consumer, Order
}

notification_domain.jdl:

entity Channel {}

domain Notification {
   entity Channel
}

commerce_application.jdl:

application Commerce {
    config {
        applicationType: monolith
        prodDatabaseType: postgres
    }
    load domain Store from 'store_domain.jdl', Notification from 'notification_domain.jdl'
    entity Store.*, Notification.*
}

Convert commerce to microservice:

application Commerce {
    config {
        applicationType: gateway
        prodDatabaseType: postgres
    }
    load domain Store from 'store_domain.jdl', Notification from 'notification_domain.jdl'
    entity Store.*, Notification.*
}
application Store {
    config {
        applicationType: microservice
        prodDatabaseType: neo4j
    }
    load domain Store from 'store_domain.jdl'
    entity Store.*
}
application Notification {
    config {
        applicationType: microservice
        prodDatabaseType: couchbase
    }
    load domain Notification from 'notification_domain.jdl'
    entity Notification.*
}

@DevAlves1993
Copy link

@DevAlves1993 your proposal seems too specific and complex.

I admit, it's really complex.

Having a common infrastructure for the entire application hurts DDD concepts, by hurting domain isolation.

The proposals I made completely ignore the infrastructure (it's done express), the most important for an application that uses the DDD principle is to have a code base that identically represents the business. I consider the infrastructure as a disposable element.

I also note that the objective of the hexagonal architecture is to completely isolate the business code base (while keeping it pure enough) from the infrastructure code base.

A proposal based on your jdl concept.

├───commerce(domain)/
│   ├───store(sub-domain)/
│       ├───usecase/
│       ├───entity/
│       ├───port/
│       ├───command/
│       ├───commandhandler/
│       ├───other/
│       ├───infrastructure/
│             ├───primary/
│                 ├───rest/
│                 ├───cli/
│             ├───secondary/
│                 ├───repository/
│                 ├───entity/
│                 ├───service/
│                 ├───config/
│                 ├───otherspackage/
│   ├───notification(sub-domain)/
│       ├───usecase/
│       ├───entity/
│       ├───port/
│       ├───command/
│       ├───commandhandler/
│       ├───other/
│       ├───infrastructure/
│             ├───primary/
│                 ├───rest/
│                 ├───cli/
│             ├───secondary/
│                 ├───repository/
│                 ├───entity/
│                 ├───service/
│                 ├───config/
│                 ├───otherspackage/

or

├───commerce(domain)/
│   ├───store(sub-domain)/
│       ├───application/
│       ├───core/
│       ├───infrastructure/
│             ├───primary/
│                 ├───rest/
│                 ├───cli/
│             ├───secondary/
│                 ├───repository/
│                 ├───entity/
│                 ├───service/
│                 ├───config/
│                 ├───otherspackage/
│   ├───notification(sub-domain)/
│       ├───application/
│       ├───core/
│       ├───infrastructure/
│             ├───primary/
│                 ├───rest/
│                 ├───cli/
│             ├───secondary/
│                 ├───repository/
│                 ├───entity/
│                 ├───service/
│                 ├───config/
│                 ├───otherspackage/

@mraible
Copy link
Contributor

mraible commented Sep 26, 2020 via email

@Blackdread
Copy link
Contributor

Blackdread commented Sep 26, 2020

I don't think to go DDD this way will be beneficial, usually you are better off using multi-module but makes things a bit more complicated if not using a bus to connect each micro-service (domain context).
Nowadays I use AxonFramework, I split core domains, events, controllers, query handler/projection, DB definition and DB access, etc in specific modules.
I just give my opinion here, I know jHipster will not have a hard dependency on another framework.

Event if DDD is a good approach to solve some complex situation within a given context, without an actual framework to support it, I think it is better to keep jHipster the way it is currently working.
And a generated code will quite often not fit the DDD domain, APIs should not be CRUD anymore, multi-module, etc; what's been generated will have to be moved, etc; Still good for monoliths/micro-service and establish a good starting structure for a project.

@DamnClin
Copy link

@DevAlves1993 For me you don't need to split things that works together in separated modules. I'm always trying to make things more cohesive (Kent beck came up with the word "cohesivating" (hoping somebody will find better) to talk about that during DDD Europe 2020).

So, in my definition, a feature is something cohesive :) (yep, a pretty bad definition I must admit) so you'll have:

|context1
|-application
|--Feature1ApplicationService.java
|--Feature2ApplicationService.java

|-domain
|-java files
|-feature1
|--java files
|-feature2
|--java files
|--feature3 (domain specific)
|--java files

|-infrastructure
|--primary
|---feature1
|----java files
|---feature2
|----java files
|--secondary
|---feature1
|----java files
|---feature2
|----java files

|context2

The number of features in each "parts" of the architecture (application, domain, primary and secondary) can vary depending on internal needs. The key is to be highly cohesive and lowly coupled. I use a lot of package (default) visibility in primary and secondary because the stuff here don't need to go outside.

In a given binary (a microservice for example) you can have multiple Bounded Contexts, each context can have his architecture. In my projects, most of them are using hexagonal architecture but some of them are just anemic CRUD (because sometimes I just need that).

As always, there is no Silver Bullet. The goal of Clean Architectures is to allow domain protection (so we can code business value here). I like the flavor of hexagonal architecture I showed at JHipster code but, like i sayed live: "This is suiting MY needs: allowing to go faster to a better solution". It may not suit yours! Switching from a classic layered architecture to this one skyrocket the team velocity, highly reduce (understand to zero) the number of bugs and cost of changes. This is my personal observation made on several projects but this comes with a cost: a mindset change for some developers and "managers" (or the likes).

@DamnClin
Copy link

@Blackdread Event if DDD is a good approach to solve some complex situation within a given context, without an actual framework to support it, I think it is better to keep jHipster the way it is currently working.

I agree with you on that! I don't think we should try to have a generator for DDD (in fact it's a nonsense) but perhaps, JHipster can help create an hexagonal architecture: those are 2 separated thinks. My goal at JHipster code was to show how I used this kind of architecture along with JHipster, I dropped the DDD parts somewhere on the way preparing this track.

I really don't know what you, JHipster contributors, can get out of that :)

@pascalgrimaud
Copy link
Member

I think there is a misunderstanding here about DDD and Hexagonal Architecture.

From @DamnClin :

I don't think we should try to have a generator for DDD (in fact it's a nonsense)

From @hdurix :

It could be interesting to explore how JHipster could also help them build the "business" of the application.

JHipster proposes a technical project base.
This ticket is here because some people are convinced that JHipster can provide an Hexagonal Architecture structure where you can apply DDD concept for your customer.
Sorry, but I don't talk about demo or hello world application.
I talk about real project, for real customer, built from scratch with JHipster. That's the main reason of the success of JHipster.

Let me resume what can be done:

Solution 1: nothing, stay like this

The user can build his business code like it is done here:

pros:

  • no more work to advance on this ticket

cons:

  • no example of hexagonal architecture structure folder
  • bad experience for new developers in the project, as they'll be confused
    • mix between technical layer and hexagonal architecture
    • default entities from JHipster will be in domain/, new entities from hexagonal architecture will be in game/infrastructure/secondary
    • default API are in web/rest, new implementations of API will be in game/infrastructure/primary

Solution 2: move to Hexagonal Architecture structure

Default JHipster application already contains User domain, which can be improved a lot to provide a good example.
What can be done (can be changed, of course): when generating a new JHipster app

  • web/rest -> infrastructure/primary
  • service -> application
  • repository -> infrastructure/secondary
  • domain -> infrastructure/secondary
  • config -> stay like this, or in infrastructure, I don't know
  • work on DTO (UserDTO and ChangePasswordDTO) -> domain

Then, I can only +1 on this:

Switching from a classic layered architecture to this one skyrocket the team velocity, highly reduce (understand to zero) the number of bugs and cost of changes.

@DamnClin
Copy link

@pascalgrimaud Problem is: users management won't be a dedicated context for all applications. In fact User is a very good example of something changing from one context to another. Nevertheless I think it's possible to create a shared user kernel and an account context. It will have to be adapted but that look like a decent example when generating the application.

The shared kernel can come with a Username value object in domain enforcing some simple rules:

  • Max length (that mean some Assertion mechanism);
  • Obfuscation in toString implementation (so you don't log usernames, GDPR etc).

You'll also find an infrastructure/primary in this shared kernel with:

  • Security configuration;
  • An adapted SecurityUtils (working with Username). I didn't moved it in PadBowl because I wanted to keep something looking a bit like a JHipster application for this demo.

In the account context you'll find account management tools:

  • WebServices to display current account, do some update etc;
  • An account aggregate (with a Username as Id);
  • Secondary adapters depending on the authentication provider. For me that mean you can delete the user persistence in JHipster when generating with keycloak (for example). I know this is a deep change, probably not to be done right now but I am doing it in the JHipster applications I use so I don't have XX account states (that mean I accept the extra latency when a user is doing an update on his account to do the update directly in keycloak).

Then comes the endpoint security question and this is a though one. Sticking to RBAC by default seems to be a good call. People needing to check who can do operations on which data will come up with solutions like kipe (as you can see this is a shared kernel in PadBowl).

JHipster can also come with a rich Assert class providing fluent assertions for the most common types (String, Numbers, Collections, ...).

There is something really important to keep in mind: this way of working works well only when working in TDD to design domains. Fact is: TDD takes a lot of work and time to master! You also have to switch from technology being the goal of a project to technology just being a tool helping you build your product. Not all teams are willing to do this switch; to learn the approaches and to tackle the final boss: DDD. This is totally fine! Giving users options to do so can be great but you can't force everybody to do that as it takes way too much time and dedication to code!

I'm turning 34, been doing code for almost 20 years and I'm still really enjoying doing so! I'm really lucky, I'm doing a work I love but many developers out there don't have that luck. They don't like coding, they are doing it because you can live doing that. You can't ask them to work full evenings and weekends just to be able to barely understand what's going on!

Of course, when I see the value we can bring to users using those tools I'd like every product out there to use them but this is not possible. We can only help people accepting help. For those ones, switching from a JHipster generated project to one using hexagonal architecture and enabling a DDD approach can be done in a matter of hours with an example. I think this is fair enough.

If JHipster can provide some easy to maintain tooling (like Assert or stuff like that) it will be awesome. Then let's continue the switch on PadBowl and add it in the documentation. In my opinion, this is better than trying to generate applications that way because it will mean maintaining both ways.

For me, this struggle is way to hard and peoples willing to do DDD will find a way! Again, this is just my advice, it doesn't worth much since I won't do this code in JHipster!

@DamnClin
Copy link

I added an actual repository implementation and some business rules to PadBowl. We can add more business rules or work to add another context to this example application. Anybody want's to do this with me live on Ippon's Twitch Channel (so we'll have the replays)?

@mraible
Copy link
Contributor

mraible commented Oct 8, 2020

Can we close this now? I don't think we're ready to make this the default option for JHipster.

@mraible mraible closed this as completed Oct 8, 2020
v7 automation moved this from In progress to Done Oct 8, 2020
@ksilz
Copy link

ksilz commented Oct 8, 2020

@DamnClin @pascalgrimaud @mraible Sorry for only chiming in now!

Here's how I structured my application with my first attempt at clean architecture. I the same set of folders, classes & interfaces in Java and Angular:

  • In Java, I use the JHipster-generated classes as my "back-end": entities, DTOs, service classes, repositories.
  • In Angular, I call my "clean REST API" through HTTP.

So here's what my folder structure looks like in Java. Everything's in a clean package. The four bounded contexts each have a folder of their own with identical structure:

|--clean
  |--app
  |--organization
  |--shared
  |--visit
    |--adapter
      |--in    
        |--VGetCustomerController.java    
      |--out    
        |--VGetCustomerAdapter.java
    |--application
      |--port
        |--in    
          |--VGetCustomerUseCase.java    
        |--out    
          |--VGetCustomerPort.java
      |--VGetCustomerService.java
    |--domain
      |--VCustomerService.java

All my Java classes in a bounded context start with the first name of that context for easier navigation (Vhere). Here are the classes mentioned above. The methods are complete, except for VGetCustomerAdapter.

public interface VGetCustomerUseCase {
  VCustomer get(Long id);
}

public interface VGetCustomerPort {
  VCustomer get(Long id);
}

public class VGetCustomerService implements VGetCustomerUseCase {
  private final VGetCustomerPort port;

  @Override
  public VCustomer get(Long id) {
    return this.port.get(id);
  }
}

@RestController
public class VCustomerController {
  private final VGetCustomerUseCase useCase;

  @GetMapping(URL_VISITS_CUSTOMER + "/{id}")
  public ResponseEntity<VCustomer> getCustomer(@PathVariable Long id) {
    VCustomer feedback = this.useCase.get(id);

    return ResponseEntity.ok().body(feedback);
  }
}

public class VGetCustomerAdapter implements VGetCustomerPort {
  @Override
  public VCustomer get(Long id) {
    // my own code for validation
    // using JHipster-generated code to load customers
  }
}

I've used this for half a year now. So here are the pros of my approach:

  • You really do get lots of smaller classes with "one reason to change".
  • Navigation is easy because in both Java & Angular, the structure is both logical and identical.
  • The necessary code synchronization between Angular & Java is easier because classes & interfaces mostly have the same name, as have the methods.

The one con is that my ports & use cases are nearly always identical. So I could do away with the port interfaces and the service classes and have my adapter implement the use case directly. I won't do this for now because there are reasons for having use cases and ports. Maybe I'll still encounter them!

@pascalgrimaud pascalgrimaud added this to the 7.0.0 milestone Oct 18, 2020
@pascalgrimaud
Copy link
Member

For all users who participated to this ticket, you can have a look at this project: https://github.com/jhipster/jhipster-lite

A generated project with JHipster Lite will help to adopt a DDD approach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
v7
  
Done
Development

No branches or pull requests