I am a beginner programmer with little experience in building complex applications. Currently I'm making a messenger using Python's FastAPI for the back-end. The main thing that I am trying to achieve within this project is a clean three-tier architecture.
My business logic layer consists of services: there's a MessageService
, UserService
, AuthService
etc., handling their corresponding responsibilities.
One of the recent additions to the app has led to the injection of an instance of ChatService
into the MessageService
. Until this time, the services have only had repositories injected in them. Services have never interacted or knew about each other.
I'm wondering if injecting one element of business layer (a service) into another one is violating the three-tier architecture in any way. To clarify things more, I'll explain why, and how I got two services overlapped:
Inside the MessageService
module, I have a method that gets all unread messages from all the chats where the currently authenticated user is a participant: get_unreads_from_all_chats
. I conveniently have a method get_users_chats
inside the ChatService
, which fetches all the chats that have the current user as a member. I can then immediately use the result of this method, because it already converts the objects retrieved from the database into the pydantic models. So I decided to inject an instance of ChatService
inside the MessageService
and implement the get_unreads_from_all_chats
method the following way (code below is inside the class MessageService):
async def get_unreads_from_all_chats(self, user: UserDTO) -> list[MessageDTO]:
chats_to_fetch = await self.chat_service.get_users_chats(user=user)
......
I could, of course, NOT inject a service into another service and instead inject an instance of ChatRepository
into the MessageService
. The chat repository has a method that retrieves all chats where the user is a participant by user's id - this is what ChatService
uses for its own get_users_chats
. But is it really a big deal if I inject ChatService
instead? I don't see any difference, but maybe somewhere in the future for some arbitrary function it will be much more convenient to inject a service, not a repository into another service. Should I avoid doing that for architectural reasons?
Does injecting a service into the service violate the three-tier architecture in any way?
2 Answers 2
Its fine.
The problem is when you have multiple services and end up with a circular dependency, the no service injected into another service rule saves you from that ever happening
eg
MessageService -> ChatService -> MessageService
When you have lots of "Services" this can happen quite easily. But if you want you can add multiple layers of services and just make the rule, no dependencies on the same layer services
for example, the classic three layers (lets not get into what three tier realy means)
Application
Service
Data
can be expanded as much as you like
Application
Use Cases
Service
Low level service
Data
This let you keep the simple rule "don't depend on things in the same layer" while taking into account that "Services" can cover a whole bunch of stuff some of which might want to depend on each other.
You can now just make some "Services" "Low level Services" or "Use Cases"
-
I don't really understand what "layers of services" means or how it would look. Where can i read about it? I don't even know what to google, so your help would be sincerely appreciated.i walk away– i walk away08/09/2025 20:29:53Commented Aug 9 at 20:29
-
More formally, it’s best if your dependencies are acyclic. This make it easier to reason about, traverse, and avoids accidental infinite loops.candied_orange– candied_orange08/09/2025 23:30:17Commented Aug 9 at 23:30
-
1
No this is obviously not a violation of the three-tier architecture (though I guess you really meant three-layer architecture, does not really matter). This kind of layering tells you only how to structure your application horizontally (i.e. Presentation layer - Business/Service layer - Persistence/Data layer), and still allows all kind of dependencies inside a single layer like the service layer.(*)
Still, horizontal layering is only one way to structure a system. Larger systems will usually also be partioned vertically. This often means multiple different applications, and/or multiple different (micro) services. Note the term service here does not refer to a single module inside a business layer, it refers to a fully self-contained web service which can be deployed and run on its own.
So in a microservice architecture, there might be a separate message service and a separate chat service - which are both web services. If you choose to design the system this way, then injecting the chat service module into the message service module would be an architectural violation, the only communication allowed between the two services would be using their web API.
Of course, vertical partioning of an application does not necessarily mean "micro services". Such structures can also be established inside monolithic applications, especially in larger ones. You can define an architectural rule that certain services belong together, and others should stay separated. Note that I emphasized above that this is your - personal - decision. There is no rule or law, not even a "best practice" which says that a system will become automatically "better" that way - whatever "better" means. Still, the larger the application gets, the more helpful additional structures become, because they allow different team members or different teams to work on isolated parts of the system, where changes to a module have a lower risk of breaking something unexpected.
On the other hand, imposing a vertical partioning on services which effectively need each other can lead to overengineering - more boilerplate code, more management effort than necessary - this is always a trade-off. In your specific case, as long as you do not have two different teams working on the message and the chat service, I would not overthink this and go ahead with injecting the second one into the first.
(*)In some articles or textbooks, the terms horizontal and vertical layers are used in the opposite way as I do. I use them with the direction of the "cut lines" between the layers in mind, presentation layer at the top, data layer at the bottom, like it is done here, and not with the relative position of the layers to each other.