0

I am trying to understand hexagonal architecture through an example of Repository. In this setup I have the following layers: framework (infrastructure) -> application -> domain.

I have User in the domain part, lets say I want to validate the User if there aren't any duplicate through a DuplicateUsernameValidator. In order to get this information I need this information from somewhere. I have added a interface UserRepository again in the domain layer, this way it can be solved in the layer above.

This is the part where it gets tricky for me. I want to implement the logic of UserRepository, but for me it doesn't make sense to implement this in the application layer, because the persistence context is in the infrastructure layer (e.g. JdbcUserRepository or JpaUserRepository). But if I understand hexagonal structure correctly I can't implement the UserRepository interface directly in my infrastructure layer, because the infrastructure layer shouldn't be aware of the domain layer.

What am I missing?

Albert Bos
  • 1,793
  • 1
  • 10
  • 24

2 Answers2

3

I think that the confusion you're facing comes from the fact that you are trying to approach an already existing Three-tier application from an Hexagonal Architecture point of view.
Let's go simple.
Let's forget for a moment of what the "Application Layer" is.
You have your hexagon that, if I understand correctly, it contains the domain of your application (User object).
Correctly you are defining a port inside your hexagon that allows you to retrieve the user from somewhere else. (I am talking about the UserRepository interface)
All the implementations of your port (JdbcUserRepository or JpaUserRepository) will represent adapters of your port and should reside outside you hexagon in order to not couple low level details of the adapters to higher level policies of your hexagon.
That's it.
Probably the difficult part is to understand what has to go inside your hexagon and what not, starting from an application with a three tier architecture (or some sort of...).
Keep inside the hexagon what is completely related to your domain and not coupled with infrastructure.
Move outside everything is related to the outside world but does not contain any business logic.
Separate and move it accordingly, everything that has both of the contexts above.

Paolo Laurenti
  • 2,364
  • 2
  • 12
  • 18
  • One thing I can't understand is what's the point of putting an implementation like `JpaUserRepository` in the infrastructure layer. For example, the `User findByName(String userName)` method would be implemented basically as `qry = em.createQuery("select u from User u where u.name = ?1"); qry.setParameter(1, userName); return qry.getSingleResult();`. It only uses the domain language and an abstract infrastructure API (JPA API and the JP-QL DSL). What's the problem with putting it in the domain layer, which would be the case in a conventional layered architecture? Isn't hexagonal overkill? – Rogério Oct 20 '16 at 19:24
  • It gets even harder to understand when you consider that many real-world queries inevitably embed business logic in them. Example: `select e from SomeEntity e where e.status = :Requested and not exists (...)`. Some of these queries are quite long and embed lots of individual business rules. Writing them outside of the domain layer doesn't seem right... – Rogério Oct 20 '16 at 19:29
  • I don't think it's overkill. There are many reasons why it's better to avoid to put infrastructure details inside the core domain. The main one is: coupling. What are the pros to put the database code inside its own adapter? Many: - separation of responsibilities: if i have to change how to retrieve data from my db, I do not have to modify a class/module relative to my business logic; - individual "developelability": e.g. a developer can work on query performance improvement while another on a bug on the business logic, without big issues – Paolo Laurenti Oct 21 '16 at 12:31
  • - individual deployability: you can deliver single modules without needing to deliver the whole application at once – Paolo Laurenti Oct 21 '16 at 12:31
  • For all the reasons above, I think it's not good to put business logic inside queries. Furthermore, if I need to change db, I would not rewrite all my business logic code. Obviously it's always a matter of trade-offs. There are times where some little part of the business logic crawls inside the queries in order to have better performances. For my experience, I learned to start as cleaner as possible, separating all the responsibilities of my code in their private contexts (hexagon and adapters). Later on I make compromises only when I have a good reason to do it. – Paolo Laurenti Oct 21 '16 at 12:42
  • I understand what you're saying, but the problem is it doesn't map to what actually happens in real-world projects (regardless of which architecture they use). Note that in the `select u from User where u.name=?1` query, `User` is a domain entity, not a table. So, this code isn't an "infrastructure detail"; in fact, it's as much "domain code" as a business method which loaded a list of users from the db (by calling a repository) and then iterated over it in a Java language loop. This code would not change unless the domain model or business rules change, as JPA allows for RDBMS portability. – Rogério Oct 21 '16 at 14:03
  • And regarding business logic in queries, as I said before, this is inevitable unless you want to seriously misuse the underlying database technology. In my real-world projects, I've seen hundreds of non-trivial JPA-QL/HQL (Java), Linq-to-SQL (C#), and the occasional SQL queries which contained domain knowledge. I just don't see any reason to avoid such queries (for obvious performance and ease of implementation reasons); and it just doesn't make sense to me putting such heavily domain-dependent code in the infrastructure layer. – Rogério Oct 21 '16 at 14:12
  • Probably we have different approaches. My "real world" is better managed trying to decouple infrastructure from business logic. Hexagonal Architecture, SOLID principles and O.O. help me a lot to not have issues with the scenarios I mentioned above. – Paolo Laurenti Oct 21 '16 at 16:58
  • Sorry, but your arguments are illogical. You are saying that having code such as the JPQL expression `select u from User u` in the domain layer would make it coupled to infrastructure, which is clearly a false statement. Proof of this is the fact that said code, when executed, may not touch an external database at all, instead returning a previously cached query result. Not to mention that it can run on a RDBMS, an in-memory DB, a NoSQL db, etc., without the developer writing/reading the code needing to know or care about it. When in reality the code mostly uses the ubiquitous domain language. – Rogério Oct 21 '16 at 17:36
  • I also think that your points do not make any sense...so, it's obvious that this discussion isn't still useful.Let's stop wasting time. I don't want to convince anyone. – Paolo Laurenti Oct 21 '16 at 17:50
  • Just note that, while I talked about specific and concrete programming scenarios, all you provided was hand-waving: "separation of responsibilities" (*which* responsibilities need to be separated in the code I used?), "if i have to change how to retrieve data from my db" (change in what way, and most importantly *when* given this kind of change is much less common than domain model or business logic changes), "query performance improvement" (as if it can be separated from the business logic implementation - in fact they go hand in hand). – Rogério Oct 21 '16 at 20:45
3

I was wondering exactly the same question and here is my conclusion : the implementation you are talking about is handled by adapter.

You have developed your business layer as an hexagon. Well! That means, your implementation depends on a contract (= the API interface) which is exposed to outside. In the same idea, your implementation uses other interfaces in order to communicate with outside (= the SPI interface you called UserRepository). Thanks to these interfaces, your hexagon is isolated from outside. When your hexagon will be instantiated, real implementations of your SPI should be passed as parameters (inversion of control).

Now, in your infra layer, you will implement your Jpa logic by implementating the adapter (adapter pattern).

Your adapter (which can be a Spring service for example) will implement your interface UserRepository and wrap your JpaUserRepository. Each method from your UserRepository will then be redirected to corresponding method of your JpaUserRepository (with or without a little adaptation).

enter image description here

OlivierTerrien
  • 1,911
  • 16
  • 28