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

hexagonal architecture #39

Merged
merged 10 commits into from
Sep 8, 2023
Merged

Conversation

baumeister25
Copy link
Contributor

Add a project structure of an hexagonal architecture to devonfw to support new projects adapting this architecture style.

@thecooldrop
Copy link

thecooldrop commented Apr 17, 2023

Hello @baumeister25, regarding your question:

If domain cannot access anything, how does the factory acess service to create a entity (for example using the postalcode service to get the user)

My view of that is that factory does not call the service. All of the data neeed for the construction is passed to the factory via method call arguments, and all of the data which is necessary for construction is retrieved in one "one-layer-up", which would basically be the use case layer in this case.

I tend to view the use case as general orchestrator, and all of the externalities should be handled there. Of course this can grow too large in time, and that is when I would decide to slowly start introducing additional helpers for use case, which are often designated as "domain services" in hexagonal architecture.

If this approach is not feasible, then nothing would speak against moving the factory one layer up, basically to colocate it with the use case and/or services. This is the point at which I would find following the architectural guidelines to be motivated more by ideals of architecture, then by actual benefits of it, since the factory obviously belongs together with entity definition.

Additionally I really dislike the current package structure, because it is cut by technical and architectural responsibilities, instead of by functionality provided by packages. While such structure is basically "one-size-fits-all" solution for starting projects it will lead to architecture where non-related things are packaged together. Further one ugly effect is that any change to functionality will almost certainly span many packages, reducing the maintainers certainty that no unrelated functionality has been changed.

@baumeister25
Copy link
Contributor Author

Hi @thecooldrop,

thanks for the feedback.
I agree to the factory. I will remove the question. In the end you're right and it should probably not use services or anything with logic. => It could be an idea to show in another article how we think the factory pattern should be implemented... But I would not discuss it here.

Regarding the package structure, my idea was that the division into business components is on a higher level (the business component a in the picture).
What would be your ideal structure beneath the business component?
Do you want to further divide the core into business functionalities using packages? Would there be further differentiation in domain, use case, ... in there or would the differentiation be just by name inside the functionality.

@aymenmastouri
Copy link

I have the understanding that the services refer to the domain, as a separate package for the domain services. That means, the service would be under the domain package.
In addition, I would make a very small simple exemple that only has a domain object, a rest interface as in-adapter and Jpa layer as out-adapter.

A factory should not use further service or do some "black magic" to
build an object. It should get all necessary data to create the object.
@thecooldrop
Copy link

Regarding the package structure, my idea was that the division into business components is on a higher level (the business component a in the picture).
What would be your ideal structure beneath the business component?

Oh, okay, now I understand. In that case I largely agree with the package structure, and find it sufficient for demonstration purposes

@baumeister25
Copy link
Contributor Author

After today's discussion with @chris-toenjes-deye we came to the conclusion to add/change the following points:

  1. Add a chapter on putting adapters and core into separate maven/gradle modules. This can be an extension to the current package structure
  2. Make the picture clearer in regards to naming. Consider packages vs classes there. E.g. Listeners are classes, everything else are packages.
  3. UseCases should implement one or multiple ports (inbound ports) to adhere to the ports and adapters concept
  4. Recommend an anemic domain model. Make the rich domain model an alternative and describe benefits and concerns. Main concern of rich modules is that they're exporting business logic into the adapters and that it might get hard for junior developers to understand which business logic to put where (use case vs domain entity)
  5. Make the service package optional. Explain that services are helpers to group busienss functionality in services.

Fabian Baumeister added 4 commits August 2, 2023 10:15
After dicussing with @chris-toenjes-deye we decided the following
points:
- Services should be marked optional. The usual business logic belongs
  inside the use case implementation. Only in cases of better
  structuring or reusable business logic, it should be moved to a
  service
- A differentiation between inbound and outbound ports is added. Inbound
  ports are used to describe use cases the core provides as interfaces
  to the inbound adapters.

Reasoning:
- In the past only outbound ports where described. Use cases should be
  used directly. This was a pragmatic approach to reduce the effort of
  implementing interfaces whith the same signature as the
  implementation. But this differs from the idea of making the
  implementation transparent to the inbound adapters.

Refs: devonfw#39
- Add inbound ports to the images
- make services optional
- Remove class names in adapters. This didn't really fit to the rest of
  the application. It did not become clear that *Listener could be
  postfixes for class names in java application. Therefore removed it
  here.
- Mor options than client on inbound side, to make the varity of cases
  visible

Ref: devonfw#39
The reason for the anemic domain models lies in two things.
First of all are the domain models returned to the adapter.
But business logic should not be moved outside.
An additional mapping should be avoided.
Furthermore in large scaled applications anemic domain models are much
easier to handle, because the business logic is not spread across use
cases, domain models and services.
Making the applicaiton easier to understand
Fabian Baumeister added 2 commits August 9, 2023 16:18
Removed the open comments.
Added the multiple monoltih question as a open issue in github devonfw#42.

Removed the records recommendation, as records cannot be inherited. That
might lead to problems with large and complex domain models.

Refs: devonfw#39
@baumeister25 baumeister25 marked this pull request as ready for review August 9, 2023 14:31
@baumeister25 baumeister25 changed the title WIP: hexagonal architecture hexagonal architecture Aug 9, 2023
Added review fixes.
Renamed all outbound to outgoing and all inbound to incoming (according
to SEAguide)
@baumeister25 baumeister25 merged commit 6ddfc90 into devonfw:main Sep 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants