I have a bank account domain as listed below. There can be SavingsAccount, LoanAccount, FixedAccount and so on. One user can have multiple accounts. I need to add a new fun
First of all why do you need AccountManipulator? It does absolutely nothing, but makes the code more complicated.
As for getting all accounts of a User, the most logical place to put this method would be in the User class. You could pass an account factory to that method, further implementation would probably depend on how you store accounts.
I'd rename 'AccountFactory' to AccountRepository
and put an extra method in it GetAccountsForUser( int userId )
which retrieves all Accounts for a specific user.
If AccountManipulator
is a webservice, then this class will use the AccountRepository
, like this:
public class AccountManipulator
{
public void FreezeAccount( int accountNr )
{
var repository = new AccountRepository();
var account = repository.GetAccount(accountNr);
account.Freeze();
repository.Save(account);
}
public ICollection<Account> GetAccountsForUser( int userId )
{
var repository = new AccountRepository();
return repository.GetAccountsForUser (userId);
}
}
if your AccountManipulator
is a Façade to your domain, I wouldn't put the account number in the constructor. I would refactor it this way:
public class AccountManipulator
{
private AccountFactory _factory;
private UserRepository _users;
public AccountManipulator(AccountFactory factory, UserRepository users)
{
_factory = factory;
_users = users;
}
public void FreezeAccount(int accountNumber)
{
var acc = _factory.GetAccount(accountNumber);
acc.Freeze();
}
public IEnumerable<IAccount> GetAccountsOf(User user) {
return _users.GetAccountIds(user).Select(_factory.GetAccount);
}
}
public interface UserRepository {
IEnumerable<int> GetAccountIds(User user);
}
In order to state if your domain is SOLID, you should analyze it with the principle:
Firstly, to really answer your question it's important to know why you need to get all user accounts? Are you:
The reason I ask is because you only need to consider the DDD aspect if it's the latter. If the reason for this 'functionality' is the former (and after reading your question I suspect it is) - I really recommend just creating a thin query service layer that gets the user's account data you need for the screen. You don't need to add the 'restrictions' of DDD for this; there are no transactions or model state changes involved. Providing this functionality doesn't have to involve the domain model at all. Just define some simple POCO DTO's and use Entity Framework to get the data and pass it back to the UI.
This is what CQRS is about; you don't need repositories, factories or aggregates to give the UI a list of accounts for the user to choose from - you would be over complicating it and making A LOT more work for yourself.
If there is a scenario that requires a single transaction over all of the user's accounts then I'd do something like:
public class AccountService : IAccountService
{
private IAccountRepository _accountRespository;
public void FreezeAllAccountsForUser(Guid userId)
{
IEnumerable<IAccount> accounts = _accountRespository.GetAccountsByUserId(userId);
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
{
foreach (IAccount account in _accounts)
{
account.Freeze();
_accountRespository.Save(account);
}
}
}
}
Where AccountService is a webservice, i.e. the Application Layer.
In summary, my advice is: Only consider DDD in the context of commands that require transactions. For fetching lists of data; create a simple query service that the UI can consume.
P.S. I've noticed the misuse of the Factory pattern in your question and some of the answers. Factories are designed to provide an object's CREATION strategy, given particular data. There shouldn't be a 'GetAccount(accountId)' method that calls the database; repositories call the database then pass data to a factory to create the object.