I am building an app that integrates Plaid API to access user bank info (logins, accounts, transactions, etc.). I'm trying to follow DDD principles.
Here is a general idea of how the Plaid API flow works:
- A user provides his email/password for some bank institution. If valid, a plaid Item is created. This object associates a user to a set of bank credentials and contains an access token which can be used to further interact with the API.
- Every plaid Item has access to a certain set of bank Accounts.
- Every bank Account has a set of Transactions
So far, I created 3 entities in my domain layer: Item, Account and Transaction. I created a repository with basic CRUD operations for each.
public class Item
{
public string Id { get; set; }
public string AccessToken { get; set; }
public string UserId { get; set; }
...
}
public class Account
{
public string Id { get; set; }
public string ItemId { get; set;
...
}
public class Transaction
{
public string Id { get; set; }
public string AccountId { get; set;
...
}
As you can see, the relationship between these entities is:
User HAS Item -> Item HAS Accounts -> Account HAS Transactions
My question is, what happens when I need to find an entity by an indirect parent? For example: GetTransactionsByItemId or GetAccountsByUserId. Based on DDD, where should this logic go?
Because of how my data is structured (No-SQL chain of 1-many relations) I know I have to do these sort of queries in multiple steps. However, I've read that a Repository should only be concerned about it's own entity so I suspect that injecting the ItemsRepository and AccountsRepository to the TransactionsRepository to add a GetTransactionsByItemId method might not be a good idea.
I also read about injecting many repositories to a Service and managing all these "joins" from inside. However, I can't come up with a name for this Service, so I'm worried that's because conceptually this doesn't make much sense.
I also read about Aggregates but I'm not sure if I recognize a root in these entities.
Another option I can think of is to try shortening relationships by adding an ItemId to every transaction for example. However, this would need to be a hack because of how I get the data from the api.
I would say your aggregation root would be an Item. If I got the structure right, Accounts cannot exist withoug Items and Transactions without account. So you could be ok just with ItemsRepository:
Than you get an Item with all the loaded data in it. The IncludesSpec is up to you: it would contain which includes should be made and includes shall be added dynamically in the repository method.
As of .net ef core 5 you can do filtered Includes, like .Include(c => c.Accounts.Where(...)), so you could further narrow the actual include down based on your requirements. You could pass another parameter which would contain this filter information.
Also your Item should expose Accounts as read-only collection (use backing field for EF) and provide a method AddAccount() so that nobody can modify your DDD item as pure entity.