I have a API call that returns a list of Payments
, each Payment
contains a User
. I need to call it, save the results in the database, fetch all the payments and users and return them all to the presenter. On the UI each payment row will also contain info about the user. So the Payment
and User
entities are highly coupled in my case.
My idea was creating PaymentRepository
and GetPaymentsUseCase
. The repository in this case has access to both Payment
and User
tables, so it can query them both and insert into both. However (correct me if I'm wrong) the clean architecture principles state that every repository should be responsible for one entity.
But in my case it really makes no sense to separate this call between 2 repos. If I decide to do so, the PaymentRepository
and UserRepository
should not access each other, so the use case would have to call both of them and pass data between them after mapping it back and forth, which looks terrible in my opinion.
Is my solution acceptable or does it break the clean architecture principles? If so, are there any suggestions on how to implement this?
Best Answer
Your repository doesn't break the "clean architecture", which identifies entities as the core, requires dependency inversion, but doesn't impose to use repositories.
But your repository seems to break the clean code principles, and in particular:
Payment
(which could change for example because of new commercial practices or new banking standards) andUser
(subject to internal policy, GDPR concerns, or identity provider requirements);Payment
's andUser
's interface, even if they do not need them (e.g. if a later use case would produce a report on the total of payments per day the last month).Your approach seems not either in line with Domain Driven Design principles, which suggest a repository for a single Aggregate :
In DDD you identify Entities, and group them into independent Aggregates that are objects that have to always be accessed via a single Aggregate Root.
But in your case, you have two independent Entities that each constitutes its own Aggregate:
Payment
as an aggregate containing a dependentUser
, since the latter may exist prior to having made any payment.User
as an aggregate with dependentPayments
, because this would mean that you could identify a payment only in relation to a user, which is not a realistic case for payment systems.Edit: additional infos
The repository concept intends to provide a level of abstraction that lets you handle persistent objects as if they were in a collection in memory, and without carrying about the db.
With this architectural pattern, you'd rather reference other aggregates by identity (value). The typical scenario is to get
Payments
by querying thePaymentRepository
. Then the payment'sUserId
would be used to get the user withUserRepostitory.getById()
. Or alternatively you'd just use thePaymentId
to query a user of a payment with by invokingUserRepostitory.getByPaymentId()
.The advantage of the identity is that it allows to decouple Entities in very complex models: passing all possible kind of objects to a repository (instead of a simple id) would cause an explosion of interface dependencies. Similarly, if related objects would systematically be loaded by the repository (as you think to expect), then you could have a snowball effect: Imagine you'd have entities representing nodes and edges of a graph. Whatever node you'd get from the repository, you'd end-up with the full graph in memory!
This being said, I don't know your model nor your constraints and I do not judge. It's up to you to decide what's the most suitable architecture. You may perfectly deviate from the rules if you have valid arguments and understand the consequences. By the way, this question about repository performance may interest you.