3

I'm working on a spring boot REST service.

It's divided into three layers: REST controllers, services, persistence/DAO.

We have multiple services and persistence repositories. We have the following situation: Service-1 needs to access the persistence layer under Service-2. One of our colleagues added a direct dependency from Service-1 to the persistence layer of Service-2 (red arrow).

I would have added the dependency so that Service-1 depends on Service-2, and not the persistence layer under Service-2 (green arrow). I cannot really explain the reasoning, but it I feel like it's a more "proper" way to introduce a dependency (to depend on higher level component in essence).

Can you give some input on the subject, and provide a good explanation in terms of software design.

enter image description here

  • 3
    What is your vertical boundaries? Without vertical boundaries it's doesn't matter. – Fabio Jun 4 '20 at 4:37
  • 4
    If service1 and service2 are separate domain areas, then the fact that one need data from another means vertical boundaries are set incorrectly. – Fabio Jun 4 '20 at 4:39
  • Is it the typical 1 controller, service, repository per DB entity? Is such a fine-grained data-centric design? – Laiv Jun 4 '20 at 13:42
4

As usual, it all depends.

If your application is basically a big ball of mud, with a single database and a complex data model underneath, where each JpaRepository is nothing more than a mirror of a given subset of tables, there is no real harm in introducing the dependency as your colleague did.

However, if you have a strict vertical separation of services (which normally leads to better maintainability) Service-1 should not know the internals - including the data storage - of Service-2. The dependency as introduced breaks encapsulation.

With depending on IService-2 instead, you could change the complete storage under Service-2 without any adverse effects in the future.

Thus, first of all, I think from a general point of view, that you are right. But what I don't know, if you are maybe overzealous, and use a sledgehammer for cracking a nut.

Another aspect: if you are right, why is it even possible to introduce a dependency from Service-1 to JpaRepository2? Think about introducing some architectural rules, and enforcing them in the project (preferrd: automatically, make the build fail.) And once again: don't be overzealous.

1

From my personal experience, for any project, I recommend you to always go through the service layer, in order to access its data, rather than accessing the data layer directly.

From the data layer, you usually cannot guarantee an object is returned, therefore all references are returned as nullable. Adding a service facade over this enables you to introduce helper methods, which e.g. throw an exception when a record is not found, and otherwise returning always a non-null object.

Other reasoning is the ability to easily change the interface of the data access layer. Since the usage would only be limited to the Service-2, making adjustments to the data layer's API is a very easy task.

Also, requiring the service as a facade can make you program less methods on your data access object. E.g. you could only introduce returning a List of objects on the repository, accepting a Set of identifiers, and the service facade would provide convenient methods to retrieve multiple or a single record:

interface SomeRepository {

    public @NotNull List<Object> getSomeObjectsByIds(@NotNull Set<Long> ids);
}

class SomeService {


    private final SomeRepository someRepository;

    // constructor omitted

    public @Nullable Object getSomeObjectById(long id) {
        return someRepository.getSomeObjectsByIds(setOf(id)).firstOrNull();
    }

    public @NotNull List<Object> getSomeObjectsByIds(@NotNull Set<Long> ids) {
        return someRepository.getSomeObjectsByIds(ids);
    }
}

Besides this, you could also introduce caching on the service layer, therefore guaranteeing a single caching mechanism, without polluting the raw DAL with the cache mechanism.

0

Assuming this is all part of the same application, there's no rule that says you should have a 1:1 mapping between services and repositories, or between any two layers for that matter. Each layer exists to abstract some detail, and you should choose the right abstraction for your use case.

The service layer likely implements some higher-level business logic on top of the entities. If Service-1 needs to reuse the logic in Service-2, then by all means do so, but if you simply need access to Entity-2, then Service-2 provides no additional value and you could just directly use the repository.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.