Refactoring code to avoid anti-pattern
I have a BusinessLayer project which has the following code. The domain object is FixedBankAccount (which implements IBankAccount).
The repository is made as a public property of the domain object and is made as an interface member. How to refactor it so that repository will not be an interface member?
The domain object (FixedBankAccount) makes use of the repository directly to store the data. Is this a violation of Single Responsibility Principle? How to correct it?
Note: The repository pattern is implemented using LINQ to SQL.
EDIT
Is the code given in the following a better approach? https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle
CODE
public interface IBankAccount
{
RepositoryLayer.IRepository<RepositoryLayer.BankAccount> AccountRepository { get; set; }
int BankAccountID { get; set; }
void FreezeAccount();
}
public class FixedBankAccount : IBankAccount
{
private RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
public RepositoryLayer.IRepository<RepositoryLayer.BankAccount> AccountRepository
{
get
{
return accountRepository;
}
set
{
accountRepository = value;
}
}
public int BankAccountID { get; set; }
public void FreezeAccount()
{
ChangeAccountStatus();
}
private void SendEmail()
{
}
private void ChangeAccountStatus()
{
RepositoryLayer.BankAccount bankAccEntity = new RepositoryLayer.BankAccount();
bankAccEntity.BankAccountID = this.BankAccountID;
accountRepository.UpdateChangesByAttach(bankAccEntity);
bankAccEntity.Status = "Frozen";
accountRepository.SubmitChanges();
}
}
public class BankAccountService
{
RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
ApplicationServiceForBank.IBankAccountFactory bankFactory;
public BankAccountService(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> repo, IBankAccountFactory bankFact)
{
accountRepository = repo;
bankFactory = bankFact;
}
public void FreezeAllAccountsForUser(int userId)
{
IEnumerable<RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == userId);
foreach (RepositoryLayer.BankAccount repositroyAccount in accountsForUser)
{
DomainObjectsForBank.IBankAccount acc = null;
acc = bankFactory.CreateAccount(repositroyAccount);
if (acc != null)
{
acc.BankAccountID = repositroyAccount.BankAccountID;
acc.accountRepository = this.accountRepository;
acc.FreezeAccount();
}
}
}
}
public interface IBankAccountFactory
{
DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount);
}
public class MySimpleBankAccountFactory : IBankAccountFactory
{
public DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount)
{
DomainObjectsForBank.IBankAccount acc = null;
if (String.Equals(repositroyAccount.AccountType, "Fixed"))
{
acc = new DomainObjectsForBank.FixedBankAccount();
}
if (String.Equals(repositroyAccount.AccountType, "Savings"))
{
acc = new DomainObjectsForBank.SavingsBankAccount();
}
return acc;
}
}
READING:
DDD - Entity state transition
https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle
Using the "Single Responsibility Principle" forces my containers to have public setters
https://softwareengineering.stackexchange.com/questions/150760/single-responsibility-principle-how-can-i-avoid-code-fragmentation
I wouldn't say that it's an anti-pattern since an anti-pattern is supposed to be a pattern in the first place (a recognizable, widespread way of doing things) and I don't know of any "Repository-in-the-Domain-object" pattern.
However, it's certainly bad practice IMO because your BankAccount domain object mixes 3 responsibilities :
Its natural and legitimate responsibility as a Domain object to freeze itself and change its status.
The responsibility to update and submit changes to a persistent store (using the accountRepository).
The responsibility to decide how a message should be sent (in that case, an email) and send it.
As a result, your Domain object is tightly coupled to too many things, making it rigid and fragile. It could change and possibly break for too many reasons.
So no anti-pattern but a violation of the Single Responsibility Principle for sure.
The last 2 responsibilities should be moved to separate objects. Submitting changes rather belongs in an object that manages the business transaction (Unit of Work) and is aware of the right time to end the transaction and flush things. The second one could be placed in an EmailService in the Infrastructure layer. Ideally, the object that does the global Freeze operation shouldn't be aware of the message delivery mechanism (by mail or something else) but should be injected with it instead, which would allow for more flexibility.
Refactoring this code so that the repository is not an interface member is easy enough. The repository is a dependency of the implementation, not the interface - inject it into your concrete class, and remove it from the IBankAccount.
public class FixedBankAccount : IBankAccount
{
public FixedBankAccount(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository)
{
this.accountRepository = accountRepository;
}
private readonly RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
public int BankAccountID { get; set; }
public void FreezeAccount()
{
ChangeAccountStatus();
}
private void SendEmail()
{
}
private void ChangeAccountStatus()
{
RepositoryLayer.BankAccount bankAccEntity = new RepositoryLayer.BankAccount();
bankAccEntity.BankAccountID = this.BankAccountID;
accountRepository.UpdateChangesByAttach(bankAccEntity);
bankAccEntity.Status = "Frozen";
accountRepository.SubmitChanges();
}
}
In regards to the second question...
Yes, the domain object is violating SRP by being aware of your persistence code. This may or may not be a problem, however; many frameworks mix these responsibilities for great effect - for example, the Active Record pattern. It does make unit testing a little more interesting, in that it requires you to mock your IRepository.
If you choose to have a more persistent-ignorant domain, you would probably best do so by implementing the Unit of Work pattern. Loaded/edited/deleted instances get registered in the Unit of Work, which is responsible for persisting changes at the end of the transaction. The unit of work is responsible for your change tracking.
How this is setup depends on the type of application you're creating and the tools you're using. I believe if working with Entity Framework, for example, you may be able to use the DataContext as your unit of work. (Does Linq-to-SQL have the notion of a DataContext as well?)
Here's an example of the Unit of Work pattern with Entity Framework 4.
The solution of Remi is much better, but a better solution IMO would be this:
1- Don't inject anything to domain objects: you do not need to inject anything into your domain entities.Not services. Not repositories. Nothing. Just pure domain model goodness
2- Let Service layer direct repositories to do SubmitChanges,... but be aware that service layer should be thin & domain objects should not be anemic
Interfaces have nothing to do directly with the single responsibility principle. You can't completely separate data access code from business logic—they have to communicate at some point! What you want to do is minimize (but do not avoid) where this happens. Make sure your database schema is logical not physical (i.e., based on predicates and not tables and columns) and that the implementation-based code (e.g., database management system connectivity drivers) are only in one place—the class responsible for talking to the database. Each entity should be represented by one class. That's it.