-2

I'm working on an accounting service. I have to generate invoice for a user and upload it to the file storage. So my service would have to use a service from file storage team. File storage service and Accounting service are on the same layer level. File storage is a generic subdomain, while accounting is a core domain. Should I do dependency inversion to avoid this coupling. what do you think? enter image description here

package io.patrick.account.invoice

interface FileStorageService {
  public void upload(String filename, File file);
}

package io.patrick.storage

interface FileStorageService {
  public void upload(Long size, String filename, File file);
}
7
  • 1
    What do you mean by this "File storage service and Accounting service are on the same layer level"? - if the accounting service is using the file storage service, that is almost the definition of them being on different layers. Commented Aug 5, 2022 at 10:28
  • @PhilipKendall Both are on application service layer. Commented Aug 5, 2022 at 10:29
  • I think you are being constrained by an overly broad definition of "application service layer". Do what it the right thing for the code, and call them "sublayers" or something if that's what you need to do to keep a UML diagram happy. Commented Aug 5, 2022 at 10:41
  • 2
    Can someone please explain why this question is being down-voted? Remember that down-votes are for bad questions, not ideas you disagree with. In my opinion, this question is perfectly on topic for this community. Commented Aug 5, 2022 at 13:25
  • @GregBurghardt I'm being hated for some reasons haha Commented Aug 5, 2022 at 13:31

3 Answers 3

1

A sentence right in your question hints at why this is a conundrum:

File storage is a generic subdomain, while accounting is a core domain.

There appears to be a misconception about what constitutes a "software layer." Layers are not defined by where you put a class. Software layers are defined by what those classes are primarily concerned with. The answer to the existential question "why do I exist?" is the primary motivator for grouping classes together in a layer. If a group of classes exist for the same basic purpose, then they could go in the same layer[1].

In your diagram, the File Storage Service is correctly associated with the infrastructure layer. The infrastructure layer is primarily concerned with interfacing with services and resources outside the control of your application. The file system is a perfect example of this.

The accounting service, which you describe as a "core domain", is primarily concerned with business logic, not file storage. As such, the Accounting Service does not belong to the infrastructure layer. The Accounting Service belongs in some application-related layer. The accounting service depends on the file storage service. Typically this dependency is represented by an interface to allow for mocking when writing unit tests.

Be aware that Domain-Driven Design is not an application architecture — it is a design philosophy. DDD does not specify where the File Storage Service and Accounting Service classes should go. This level of detail is captured in an application architecture, like Onion Architecture, Clean Architecture, etc. DDD helps you organize business logic as a means to manage complexity. One way you accomplish this is by removing data storage logic from your core businesses classes.

Many application architectures have an "infrastructure" layer, because this separation is desirable in general, not just with Domain-Driven Design. DDD does not prescribe exactly where the accounting service should go. This is driven by application architecture, not some business logic design philosophy.

Your question arises from organizing and grouping classes incorrectly. The accounting service, which is primarily concerned with business logic and coordination, is not an infrastructure class. The accounting service belongs in a different layer that depends on the infrastructure layer. This resolves your question because you no longer need inversion of control within the same layer. The file storage and accounting services reside in different layers.

The layer containing the accounting service should define an interface for file storage that is implemented by the real file storage service in the infrastructure layer. This follows the Dependency Inversion principal (the "D" in "SOLID"). Dependencies across modules should rely on abstractions. Interfaces and abstract classes are some of the most common abstractions used at the boundaries between application modules.


  1. On a technical level, classes are often grouped into layers based on their dependencies, but I would argue that dependencies are discovered after answering the existential question: "why do I exist?" Only after discovering why a class exists can you identify the other objects it must collaborate with to fulfill its primary purpose in the application, and therefore which layer it belongs to.
2
  • Thanks for the answer. btw I have one more question. As I read this "As such, the Accounting Service does not belong to the infrastructure layer." I think I should add more detail. So accounting service not just upload invoice file to storage service, but it also needs to keep the id, url (where the file belong to) and status (it needs to be check and approve by a user later). So actually I also keep some data and save it to the database. So is this wrong design? what do you think. Commented Aug 5, 2022 at 13:47
  • @Patrick: all of this is fine to do in the accounting service, but the logic for saving the data still belongs in the infrastructure layer. It means your accounting service depends on more than one abstraction that should be implemented by the infrastructure layer. I read some place that classes can be split into two main categories: collaborators and algorithms. The accounting service class is a "collaborator". The file storage service is an "algorithm". I wish I could find the source for that idea, though. Commented Aug 5, 2022 at 14:00
0

Do you want to be able to unit test your accounting service with a FileStorageServiceMock? And do you need dependency injection for this? If yes, then the answer should be self-evident. If no, then you probably don't need it.

In a layered system a typical motivation for DI is to avoid dependencies in certain directions. However, it is usually not the only one. Think about the purpose, for what you need DI - DI is not an end in itself, it is a means to an end.

-1

I don't get it, why do you need a Dependency inversion here?

If you go through the concept of dependency of inversion, "High level modules must not depend on the low level details", second, "abstraction should not depend on details".

Considering your scenario, if FileStorage had sub-types of FileBased Storage, such as: PlainTextFileStorage, CSVFileStorage, ExcelFileStorage. So, you would have had to create one abstraction of FileStorage and rest of the FileStorage would implement/extend the abstraction.

Similarly, in AccountService, if there are multiple account types, such as: SavingAccount, CurrentAccount, StudentAccount, BusinessAcount, then dependency inversion principle would have been applicable.

I guess, there isn't a room for dependency inversion or injection in your provided scenario.

To make room for DI, you need to introduce abstraction between both the services. Currently, there is no commonality or abstraction between FileStorageService and AccountService. In order to implement DI, you need to make both these services sub-modules or level low modules of an abstraction. Service could be an abstraction.

For further, read this: https://stackify.com/dependency-inversion-principle/

3
  • Sorry, but I have some trouble to understand this answer - it says one does not need DI here because the components are already designed for DI? Sounds contradictory to me. Commented Aug 5, 2022 at 14:04
  • You got me wrong. In your scenario, there is no commonality or abstraction between FileStorageService and AccountService. In order to implement DI, you need to make both these services sub-modules or level low modules of an abstraction. Service could be an abstraction. Currently, I don't find any abstraction in your scenario. Commented Aug 5, 2022 at 14:13
  • No idea why you say "your scenario", I did not ask the question. Commented Aug 5, 2022 at 16:17

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.