0

Our Rest service is a classic Controller-Service-Repository architecture implemented in Java with Spring. As described here, it helps with separation of concerns and unit testing. Sometimes, our services need to call each other, resulting in a circular dependency.

Example: Two domains: Organizations with users. An organization can have multiple users. Classes: OrgControler, OrgService, OrgRepo. UserController, UserService, UserRepo

Problematic scenario: OrgService can call UserService to receive the users belonging to the Org. UserService wants to call OrgService to change updated_time in the DB. If this were microservices, there would be no problem. The services would just call the APIs, but since we are in a Java Monolith, it is causing a circular dependency.

Today, we are resolving it by calling OrgRepo directly from UserService, but this feels wrong.

How would you resolve it?

2
  • 1
    Base on your org logic. In some cases, when I just want to keep each service can only query their own table, I wont let UserService query into OrgRepo. Instead, I will try to create a third service to do both things. Commented Oct 8, 2024 at 4:04
  • What is 'updated_time' in the domain context? You indicate it's in the Org domain but not what it represents. Commented Oct 8, 2024 at 16:53

2 Answers 2

1

If you strictly enforce a 1:1 relationship between each Service and Repository (and each Repository maps to one database table) what you will end up with is database-driven design, where the structure of your code is determined not by the domain but by the schema of the persistence layer.

Given that persistence is nothing more than an implementation detail, this structure is less than ideal. You should be able to modify your schema, for example by normalizing, denormalizing, switching between SQL and NoSQL, etc., without modifying code outside the persistence layer.

To phrase it another way, a Service shouldn't care whether the data it retrieves comes from one table or ten tables or a flat file system, or a network call, or an in-memory cache, or anywhere else. To a Service, data is data and its origin is irrelevant.

With all that being said, it sounds like maybe your domain includes the concept of something like a TimestampService which needs to be implemented.


Bob Martin describes this concept of Domain-Driven Design thusly.

For a very long time there was an idea that the domain model was the database model. They were the same. You'd come up with an entity-relationship diagram that would describe the schema of the database, and that was your domain model.

The problem with that viewpoint is that it has no behavior, whereas the goal of the business is to implement behaviors. Now, you might need data structures to do that; but we want to know what the behaviors are. So, in the Clean Architecture, in Ports & Adapters, in all of these architectures, the business objects contain behavior... They might use data structures, but the business objects represent behavior.

...

Yes, you need to be able to sub-divide the data. You've got to be able to have a good data model; but the data model is not the behavior model. The data model is not the business model. The data model is not the domain.

Sign up to request clarification or add additional context in comments.

Comments

0

You need to make sure that your dependencies point in one direction (controller -> service -> repository). Then you won't have cycles.


To do this,

  1. Don't call services from other services.
  2. Feel free to call multiple repositories from your services.

P.S. Services contain business logic, including transaction management logic. Accordingly, if you have 2 repositories and you need to do some action in these repositories in one transaction, then the service is the place that will wrap the work with these repositories in one transaction.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.