I have created a class Accountthat has the common fields for the account. It looks likes this:
public class Account
{
public string Name { get; set; }
public double Balance { get; set; }
public Account(double Credit)
{
Balance = Credit;
}
}
Then I created a Withdrawal class where the withdrawal take place. This withdrawal class inherits from the Account Class. I have created an interface where the withdrawal class inherits from. The main reason for having a constructor in the account class is, when a customer first opens an account with the bank, an amount must be credited into the customer's account as a form of thank you for opening an account with us. This amount is not fixed, it depends on the type of account that the customer opens.
My challenge now is anytime I am executing the withdrawal class, the constructor of the base class gets executed and the thank you gift replaces any amount the customer has in the balance.
What I really want to do is for the constructor to get executed pmce and the withdrawal class should work without the base class constructor firing up.
I will also like to know if I have violated any SOLID principle, especially in the logic method . Will I be able to unit test withdrawal1 method most especially.
This is my Withdrawal class
public class Withdrawal : Account, IAccountWithdrawal
{
public Withdrawal() : base(400)
{
}
public void Withdrawal1(double Amount)
{
bool Result = Logic(Amount);
}
public bool Logic (double Amt)
{
if (Amt <= Balance)
{
Balance -= Amt;
return true;
}
else
{
return false;
}
}
}
An object should encapsulate its own data and operations that work on this data only.
So the Account class should hold the Balance as data. As noted above, use decimal for financial calculations. It should also hide the balance from any outside manipulation that does not go through the appropriate methods, so it should be private. One could envision two methods that change the balance: Withdraw and Deposit. Because the Balance is private, we will also need a GetCurrentBalance method to read the current balance. The constructor takes all parameters that are required to create a valid new account (note that Balance will default to 0).
public class Account {
public Account(string name) {
Name = name;
}
// Name is required for a valid account, so it is part of the constructor
public string Name { get; private set; }
// Balance is critical, so it is private to prevent direct manipulation from outside
private decimal Balance { get; set; }
public decimal GetCurrentBalance() {
return Balance;
}
public void Deposit(decimal amount) {
Balance += amount;
// here would be a good place to write audit logs ...
}
public void Withdraw(decimal amount) {
if (Balance < amount) {
throw new InvalidOperationException($"The account '{Name}' can not be overdrawn.")
}
Balance -= amount;
}
}
But for many use cases, there will be more than one object involved. Let us look at the "Create new account and deposit the initial gift" use case. There are actually two accounts involved: the account to be created and the account of the bank from which the gift should be transferred.
In such cases where more than one object is involved, it is better to have a separate DomainService class that handles the business logic. This makes the code easier to understand because there are no hidden calls between the involved objects. The DomainService provides a specific method for every business case, and takes all involved objects as parameters. In our example, there is an explicit OpenNewAccount method that creates the new account and transfers the initial gift. Note that the AccountDomainService does not care how the required accountToWithdrawGift is to be retrieved, it just takes it as parameter. Also note that because we know the exact use case, we can generate meaningful error messages.
public AccountDomainService {
public Account OpenNewAccount(string name, Account accountToWithdrawGift, decimal giftAmount) {
// create the new Account
var newAccount = new Account(name);
// now handle the gift
try {
accountToWithdrawGift.Withdraw(giftAmount);
newAccount.Deposit(giftAmount);
}
catch (InvalidOperationException ex) {
throw new InvalidOperationException (
$"The account '{accountToWithdrawGift.Name}' has insufficient balance " +
$"to withdraw the gift amount '{giftAmount}'.", ex
);
}
return newAccount;
}
}
The business logic is called by an ApplicationService which faces the outside (e.g. UI, webservice). Here is a good place to validate any user inputs and handle database access.
public class AccountApplicationService {
private DbContext _dbContext;
private AccountDomainService _domainService;
// you can use Dependency Injection to provide the appropiate dbContext and domainService
public AccountApplicationService(DbContext dbContext, AccountDomainService domainService) {
_dbContext = dbContext;
_domainService = domainService;
}
public Account OpenNewAccount(OpenNewAccountCommand command) {
// validate user input ...
Validate(command);
// load bank account from the database
var accountToWithdrawGift = _dbContext.Accounts.Single(a => a.Name == "Bank Ldt.");
const decimal giftAmount = 400.00M;
// call domain service to execute the business case
var newAccount = _domainService.OpenNewAccount(command.Name, accountToWithdrawGift, giftAmount);
// persist new Account in the Database
_dbContext.Accounts.Add(newAccount);
_dbContext.SaveChanges();
return newAccount;
}
}
With this structure, the business logic encapsulated in Account and AccountDomainService can be unit tested in full without the need to mock the database. The database required by AccountApplicationService can be mocked and injected in the constructor.
Related
I'm creating an application with employee and employer as a domain objects.
Both of them have a reference to User object where I store password and other account related stuff.
Example:
public class Employee
{
public Guid EmployeeId { get; set; }
public User User { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string About { get; set; }
...
//other properties
}
public class Employer
{
public Guid EmployerId { get; set; }
public User User { get; set; }
public string CompanyName { get; set; }
public string CompanyDescription { get; set; }
public string FoundedYear { get; set; }
...
//other properties
}
public class User
public Guid UserId { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
...
//other properties
}
I'm also using application services where a method represents a single use case.
Let's say I have RegisterEmpolyee method that should save employee to database set his role to "Employee" and send verification email.
This is my code right now. I'm using AspNet.Core.Idenity.UserManager to create user account:
public async Task<EmployeeDto> RegisterEmployee(RegisterEmployeeDto employee)
{
var validateResult = _validatorService.Validate(employee);
if (!validateResult.IsValid)
throw new ServerException
("RegisterEmployeeDto is not valid", validateResult.GetErrors());
await _db.BeginTransactionAsync();
var newUser = new User { UserName = employee.Email, Email = employee.Email };
var userCreationResult = await _userManager.CreateAsync(newUser, employee.Password);
if (!userCreationResult.Succeeded)
{
var userCreationErrors = userCreationResult.GetIdentityResultErrors();
throw new ServerException("Error during create User account.", userCreationErrors);
}
await _roleService.AddUserToRoleAsync(newUser.Id, ApplicationRoles.Employee);
var verificationCode = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
newUser.VerificationCode = verificationCode;
await _emailService.SendActivationEmail(newUser.Email, newUser.Id, verificationCode);
var newEmployee = new Employee(employee.Name, employee.Surname, newUser);
await _db.Employees.AddAsync(newEmployee);
await _db.CompleteAsync();
var employeeDto = _mapper.Map<Employee, EmployeeDto>(newEmployee);
_db.CommitTransaction();
return employeeDto;
}
And here are my questions:
Does this code and my approach are fine according to DDD?
Should I extract creation of employee to domain service? Or maybe factory? And if so should I call repository method from there? (I mean service of course)
Let's say should extract creation of employee to domain service. Should I create User internally then?
Like this:
public async Task<Employee> CreateEmployee(RegisterEmployeeDto employee)
{
var newUser = new User { UserName = employee.Email, Email = employee.Email };
var userCreationResult = await _userManager.CreateAsync(newUser, employee.Password);
if (!userCreationResult.Succeeded)
{
var userCreationErrors = userCreationResult.GetIdentityResultErrors();
throw new ServerException("Error during create User account.", userCreationErrors);
}
var newEmployee = new Employee(employee.Name, employee.Surname, newUser);
//Should I call repository here?
await _db.Employees.AddAsync(newEmployee);
await _db.CompleteAsync();
return newEmployee;
}
Or maybe pass User as a parameter?
And last question: Where is a right place to checking if user I want to create exist or not? Is Application service appropriate place to do so?
Thank you in advance for answers.
From what I see, User, Employee and Employer are Aggregate roots (AR).
Does this code and my approach are fine according to DDD?
In DDD it's not recommended that an Aggregate have references to other Aggregates other than by ID. Your Employee and Employer AR have such a bad reference so it is not OK. Instead Employee and Employer should contain only a UserId field.
Should I extract creation of employee to domain service? Or maybe factory? And if so should I call repository method from there? (I mean service of course)
From what I can see you have a complex process of creating multiple Aggregates. In DDD you cannot do this atomically, inside a single transaction. Instead, every Aggregate is created/mutated in its own transaction. There is however a tactical pattern of coordinating a long process: Saga/Process manager.
You should define a process of registering an employee as a Saga: RegisterEmployee. This process should have an interface with these methods: create, start, continue. The create method receive all the data it needs to start process. The start method tries to run the individual steps (like createEmployee, createUser etc); if the start method is run again, it should continue from where has stopped, so the Saga should record its status.
The architecture can be made better by making the command on Aggregates as idempotent. In this way, when a Saga restarts it can send again all the commands to the Aggregates; this effectively makes the Saga very simple.
Let's say should extract creation of employee to domain service. Should I create User internally then?
That domain service is in fact the Saga from the previous step. The Saga however should not contain logic that belongs to the Aggregates! Be carefully to not make your domain model anaemic. The Saga should contain only coordinating logic!
And last question: Where is a right place to checking if user I want to create exist or not? Is Application service appropriate place to do so?
What means that an User already exists? There is already an user with that username? If yes, then the simplest solution is to have an unique index on the username column, if possible. If it's not possible (i.e. you have sharding enabled) then you can have another Saga that checks for duplicates and reports to an Admin or something.
I make a Booking form for restaurant, which asks for the name of the restaurant, the date of the meal and the number of person.
I have a booking class, which has an ID, an ID of the restaurant, a date and a number of people :
public class Booking
{
public int Id { get; set; }
public int IDRestaurant{ get; set; }
[CustomPlaceValidator]
public int Nbpeople { get; set; }
[CustomDateValidator]
public DateTime Date { get; set; }
}
As well as a Resto class, which has an ID, a name, phone number and a number of table :
public class Resto
{
public int Id { get; set; }
[Required(ErrorMessage = "Le nom du restaurant doit être saisi")]
public string Nom { get; set; }
[Display(Name = "Téléphone")]
[RegularExpression(#"^0[0-9]{9}$", ErrorMessage = "Le numéro de téléphone est incorrect")]
public string Telephone { get; set; }
[Range(0, 9999)]
public int Size { get; set; }
}
I would like to make a validation to check with each new reservation, that the restaurant is not full.
To do this, when validating the "Number of persons" field of the Booking, I need the value of the "restaurant name" field and the value of the "date" field, and then retrieve all the bookings on this Restaurant at that date, and check whether the sum of the number of persons is much lower than the capacity of the restaurant.
public class CustomPlaceValidator : ValidationAttribute
{
private IDal dal = new Dal();
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
int nb = 0;
if (dal.GetAllBooking() != null)
{
foreach (var booking in dal.GetAllBooking())
nb += booking.Nbpeople;
if (nb ..... ) return ValidationResult.Success;
return new ValidationResult("The restaurant is full for this date.");
}
return ValidationResult.Success;
}
}
(It's a draft, the tests are not finished obviously)
How can I have the value of the other proprieties for my validation ?
This is not appropriate for a validation attribute. First, a validation attribute should be independent, or at least self-contained. Since the logic here depends on two different properties (the number of people and the date of the booking) a validation attribute would require too much knowledge of the domain in order to perform the necessary validation. In other words, it's not reusable, and if it's not reusable, then there's no point in using an attribute.
Second, a validation attribute should not do something like make a database query. The controller alone should be responsible for working with your DAL. When you start littering database access across your application, you're going to start running into all sorts of issues in very short order. If you use a DI container to inject your DAL where it needs to go, it's less problematic to use it outside of the controller, but importantly, attributes really don't play well with dependency injection. You can make it work with some DI containers, but it's never easy and you're probably going to regret it later. So, again, this really shouldn't be something a validation attribute handles.
The best approach in my opinion is to simply create a private/protected method on your controller to handle this validation. Something like:
public void ValidateCapacity(Booking booking)
{
var restaurant = dal.GetRestaurant(booking.IDRestaurant);
var existingBookings = dal.GetBookings(booking.IDRestaurant, booking.Date);
var available = restaurant.Size - existingBookings.Sum(b => b.Nbpeople);
if (booking.Nbpeople > available)
{
ModelState.AddModelError("Nbpeople", "There is not enough capacity at the restaurant for this many people on the date you've selected");
}
}
Then, in your post action for the booking, simply call this before checking ModelState.IsValid.
I'm looking at this question: Group validation messages for multiple properties together into one message asp.net mvc
My guess is something like:
public class Booking
{
public int Id { get; set; }
public int IDRestaurant{ get; set; }
[CustomPlace("IDRestaurant", "Date", ErrorMessage = "the restaurant is full")]
public int Nbpeople { get; set; }
[CustomDateValidator]
public DateTime Date { get; set; }
}
and the custom validation:
public class CustomPlaceAttribute : ValidationAttribute
{
private readonly string[] _others
public CustomPlaceAttribute(params string[] others)
{
_others= others;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// TODO: validate the length of _others to ensure you have all required inputs
var property = validationContext.ObjectType.GetProperty(_others[0]);
if (property == null)
{
return new ValidationResult(
string.Format("Unknown property: {0}", _others[0])
);
}
// This is to get one of the other value information.
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
// TODO: get the other value again for the date -- and then apply your business logic of determining the capacity
}
}
However, it feels a bit messy to do a database call for the validationAttribute though
What you are asking for is cross-property validation. If you are not strongly opposed to implementing an interface on your data objects you should take a look at the following:
https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.ivalidatableobject.aspx
A simple example implementation for a small rectangle class where we want its area not to exceed 37 (whatever that unit is).
public class SmallRectangle : IValidatableObject
{
public uint Width { get; set; }
public uint Height { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var area = Width * Height;
if (area > 37)
{
yield return new ValidationResult($"The rectangle is too large.");
}
}
}
Alternatives
The the second parameter of the IsValid function in your ValidationAttribute provides you with the ValidationContext which has the property ObjectInstance which you can cast to your object type and access its other members. That, however, will make your validation attribute specific to your class. I would generally advise against that.
You could also opt to use a different validation approach altogether such as using a validation library such as FluentValidations, see:
https://github.com/JeremySkinner/FluentValidation
A different perspective
Last but not least I would like to note that usually validation should be used to validate the integrity of the data. A booking request which requests more seats than available is not invalid. It can not be granted, but it is a valid request which will, unfortunately, be answered with a negative result. To give that negative result is, in my opinion not the responsibility of the validation, but the business logic.
Good afternoon fellow stackers (or overflowers, whichever you prefer), this is more of a cleanliness and convenience issue than anything else but I can't imagine that I'm the only one who's ever wondered about it so here we go...
I've got a basic OData enabled WCF Data Service class that's using my Entity Framework data context.
[JsonpSupportBehavior]
public class ControlBindingService : DataService<MyDataContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
config.DataServiceBehavior.AcceptCountRequests = true;
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
}
protected override MyDataContext CreateDataSource()
{
if (HttpContext.Current == null)
throw new InvalidOperationException("The WCF Data Services implementation must be hosted in IIS.");
string username;
if (HttpContext.Current.User.Identity.IsAuthenticated)
username = HttpContext.Current.User.Identity.Name;
else
{
// The request didn't have user identity, attempt to find UserName in the
// request header before returning 401 to the caller.
if (!String.IsNullOrEmpty(HttpContext.Current.Request.Headers["UserName"]))
{
username = HttpContext.Current.Request.Headers["UserName"];
// REVIEW: We should validate user before passing it to the datacontext.
}
else
throw new DataServiceException(401, "Client did not pass required authentication information.");
}
return MyDataContext.GetInstance(username);
}
[WebGet]
public List<DailyKeyPerformanceIndicator> GetResourceKPIs(
int resourceId, string jsonStart, string jsonEnd, int scenarioId)
{
DateTime start = jsonStart.DeserializeJson<DateTime>();
DateTime end = jsonEnd.DeserializeJson<DateTime>();
if (scenarioId < 1)
{
scenarioId = CurrentDataSource.GetScenarios()
.Single(s => s.IsProduction).ScenarioID;
}
return CurrentDataSource.GetDailyResourceKPI(
scenarioId, start, end, resourceId);
}
}
The data context is just a standard (code-first) DbContext implementation with properties exposing the entity sets, etc..
However, we also have methods on there to expose some tables that we wanted to enforce some constraints upon. Specifically (see code below), we want to know what the caller wants to use the data for so we can return only the appropriate results. For example, if the caller wants to get rows from the employees table--they may want to get all rows, or only rows that they have update privileges for.
[Serializable]
public partial class MyDataContext : DbContext
{
static MyDataContext()
{
Database.SetInitializer<MyDataContext>(null);
}
public MyDataContext()
: base("name=MyDBString")
{ }
// Standard table properties...
public DbSet<User> Users
{
get { return this.Set<User>(); }
}
public DbSet<UserSetting> UserSettings
{
get { return this.Set<UserSetting>(); }
}
public DbSet<SettingDefinition> SettingDefinitions
{
get { return this.Set<SettingDefinition>(); }
}
// Restricted table methods...
public DbSet<Client> GetClients(
DatabasePermissions perms = DatabasePermissions.Select)
{
// getPermissibleSet is a method in a helper class that does some
// magical querying and produces a filtered DbSet.
return getPermissibleSet<Client>(perms);
}
public DbSet<Employee> GetEmployees(
DatabasePermissions perms = DatabasePermissions.Select)
{
// getPermissibleSet is a method in a helper class that does some
// magical querying and produces a filtered DbSet.
return getPermissibleSet<Employee>(perms);
}
}
Now to the root of the issue... What I'd like to avoid having to do is writing a [WebGet] for each and every "restricted table method" on my data context. The reason is really nothing more than redundancy--the [WebGet] method would end up being a direct pass-through to the data context.
So in summary, I'd say what I'm basically looking to do is to mark methods from my data context class that WCF will expose in the same way it does for my DbSet properties. Any takers?
Thanks! J
This is an interesting problem. I'm trying to do similar things. This is kind of throwing a dart here but have you tried something like this? You should probably separate the generics out so you don't create a unique context with each type, but it seems like you should be able to get rid of the duplicate code with generics.
[Serializable]
public partial class MyDataContext<T> : DbContext where T : class
{
static MyDataContext()
{
Database.SetInitializer<MyDataContext>(null);
}
public MyDataContext()
: base("name=MyDBString")
{ }
// Standard table properties...
public DbSet<T> SettingDefinitions
{
get { return this.Set<T>(); }
}
// Restricted table methods...
public DbSet<T> GetClients(
DatabasePermissions perms = DatabasePermissions.Select)
{
// getPermissibleSet is a method in a helper class that does some
// magical querying and produces a filtered DbSet.
return getPermissibleSet<T>(perms);
}
}
I have an existing bank application classes as shown below. The banks account can be of SavingsBankAccount or FixedBankAccount. There is an operation called IssueLumpSumInterest. For FixedBankAccount, the balance need to be updated only if the owner of the account has no other account.
This demands the FixedBankAccount object to know about other accounts of the account owner. How to do this by following SOLID/DDD/GRASP/Information Expert pattern?
namespace ApplicationServiceForBank
{
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 IssueLumpSumInterest(int acccountID)
{
RepositoryLayer.BankAccount oneOfRepositroyAccounts = accountRepository.FindByID(p => p.BankAccountID == acccountID);
int ownerID = (int) oneOfRepositroyAccounts.AccountOwnerID;
IEnumerable<RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == ownerID);
DomainObjectsForBank.IBankAccount domainBankAccountObj = bankFactory.CreateAccount(oneOfRepositroyAccounts);
if (domainBankAccountObj != null)
{
domainBankAccountObj.BankAccountID = oneOfRepositroyAccounts.BankAccountID;
domainBankAccountObj.AddInterest();
this.accountRepository.UpdateChangesByAttach(oneOfRepositroyAccounts);
//oneOfRepositroyAccounts.Balance = domainBankAccountObj.Balance;
this.accountRepository.SubmitChanges();
}
}
}
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;
}
}
}
namespace DomainObjectsForBank
{
public interface IBankAccount
{
int BankAccountID { get; set; }
double Balance { get; set; }
string AccountStatus { get; set; }
void FreezeAccount();
void AddInterest();
}
public class FixedBankAccount : IBankAccount
{
public int BankAccountID { get; set; }
public string AccountStatus { get; set; }
public double Balance { get; set; }
public void FreezeAccount()
{
AccountStatus = "Frozen";
}
public void AddInterest()
{
//TO DO: Balance need to be updated only if the person has no other accounts.
Balance = Balance + (Balance * 0.1);
}
}
}
READING
Issue in using Composition for “is – a “ relationship
Implementing Business Logic (LINQ to SQL)
http://msdn.microsoft.com/en-us/library/bb882671.aspx
Architecting LINQ to SQL applications
Exploring N-Tier Architecture with LINQ to SQL
http://randolphcabral.wordpress.com/2008/05/08/exploring-n-tier-architecture-with-linq-to-sql-part-3-of-n/
Confusion between DTOs (linq2sql) and Class objects!
Domain Driven Design (Linq to SQL) - How do you delete parts of an aggregate?
The first thing I noticed was the improper use of the bank account factory. The factory, pretty much as you have it, should be used by the repository to create the instance based on the data retrieved from the data store. As such, your call to accountRepository.FindByID will return either a FixedBankAccount or SavingsBankAccount object depending on the AccountType returned from the data store.
If the interest only applies to FixedBankAccount instances, then you can perform a type check to ensure you are working with the correct account type.
public void IssueLumpSumInterest(int accountId)
{
var account = _accountRepository.FindById(accountId) as FixedBankAccount;
if (account == null)
{
throw new InvalidOperationException("Cannot add interest to Savings account.");
}
var ownerId = account.OwnerId;
if (_accountRepository.Any(a => (a.BankUser.UserId == ownerId) && (a.AccountId != accountId)))
{
throw new InvalidOperationException("Cannot add interest when user own multiple accounts.");
}
account.AddInterest();
// Persist the changes
}
NOTE: FindById should only accept the ID parameter and not a lambda/Func. You've indicated by the name "FindById" how the search will be performed. The fact that the 'accountId' value is compared to the BankAccountId property is an implementation detail hidden within the method. Name the method "FindBy" if you want a generic approach that uses a lambda.
I would also NOT put AddInterest on the IBankAccount interface if all implementations do not support that behavior. Consider a separate IInterestEarningBankAccount interface that exposes the AddInterest method. I would also consider using that interface instead of FixedBankAccount in the above code to make the code easier to maintain and extend should you add another account type in the future that supports this behavior.
From reading your requirement, here is how I would do it:
//Application Service - consumed by UI
public class AccountService : IAccountService
{
private readonly IAccountRepository _accountRepository;
private readonly ICustomerRepository _customerRepository;
public ApplicationService(IAccountRepository accountRepository, ICustomerRepository customerRepository)
{
_accountRepository = accountRepository;
_customerRepository = customerRepository;
}
public void IssueLumpSumInterestToAccount(Guid accountId)
{
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
{
Account account = _accountRepository.GetById(accountId);
Customer customer = _customerRepository.GetById(account.CustomerId);
account.IssueLumpSumOfInterest(customer);
_accountRepository.Save(account);
}
}
}
public class Customer
{
private List<Guid> _accountIds;
public IEnumerable<Guid> AccountIds
{
get { return _accountIds.AsReadOnly();}
}
}
public abstract class Account
{
public abstract void IssueLumpSumOfInterest(Customer customer);
}
public class FixedAccount : Account
{
public override void IssueLumpSumOfInterest(Customer customer)
{
if (customer.AccountIds.Any(id => id != this._accountId))
throw new Exception("Lump Sum cannot be issued to fixed accounts where the customer has other accounts");
//Code to issue interest here
}
}
public class SavingsAccount : Account
{
public override void IssueLumpSumOfInterest(Customer customer)
{
//Code to issue interest here
}
}
The IssueLumpSumOfInterest method on the Account aggregate requires the Customer aggregate to help decide whether interest should be issued.
The customer aggregate contains a list of account IDs - NOT a list of account aggregates.
The base class 'Account' has a polymorphic method - the FixedAccount checks that the customer doesn't have any other accounts - the SavingsAccount doesn't do this check.
2 min scan answer..
Not sure why there is a need for 2 representations of a BankAccount
RepositoryLayer.BankAccount and DomainObjectsForBank.IBankAccount. Hide the persistence layer coupled one.. deal with just the domain object in the service.
Do not pass/return Nulls - I think is good advice.
The finder methods look like the LINQ methods which select items from a list of collection. Your methods look like they want to get the first match and exit..in which case your parameters can be simple primitives (Ids) vs lambdas.
The general idea seems right. The service encapsulates the logic for this transaction - not the domain objects. If this changes, only one place to update.
public void IssueLumpSumInterest(int acccountID)
{
var customerId = accountRepository.GetAccount(accountId).CustomerId;
var accounts = accountRepository.GetAccountsForCustomer(customerId);
if ((accounts.First() is FixedAccount) && accounts.Count() == 1)
{
// update interest
}
}
Things that strike me as weird:
Your IBankAccount has a method FreezeAccount, but I presume that all accounts would have quite similar behavior? Perhaps a BankAccount class is warranted that implements some of the interface?
AccountStatus should probably be an enum? What should happen if an account is "Forzen"?
I'm new to DDD, and I'm trying to apply it in real life. There is no questions about such validation logic, as null check, empty strings check, etc - that goes directly to entity constructor/property. But where to put validation of some global rules like 'Unique user name'?
So, we have entity User
public class User : IAggregateRoot
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// other data and behavior
}
And repository for users
public interface IUserRepository : IRepository<User>
{
User FindByName(string name);
}
Options are:
Inject repository to entity
Inject repository to factory
Create operation on domain service
???
And each option more detailed:
1 .Inject repository to entity
I can query repository in entities constructor/property. But I think that keeping reference to repository in entity is a bad smell.
public User(IUserRepository repository)
{
_repository = repository;
}
public string Name
{
get { return _name; }
set
{
if (_repository.FindByName(value) != null)
throw new UserAlreadyExistsException();
_name = value;
}
}
Update: We can use DI to hide dependency between User and IUserRepository via Specification object.
2. Inject repository to factory
I can put this verification logic in UserFactory. But what if we want to change name of already existing user?
3. Create operation on domain service
I can create domain service for creating and editing users. But someone can directly edit name of user without calling that service...
public class AdministrationService
{
private IUserRepository _userRepository;
public AdministrationService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void RenameUser(string oldName, string newName)
{
if (_userRepository.FindByName(newName) != null)
throw new UserAlreadyExistException();
User user = _userRepository.FindByName(oldName);
user.Name = newName;
_userRepository.Save(user);
}
}
4. ???
Where do you put global validation logic for entities?
Thanks!
Most of the times it is best to place these kind of rules in Specification objects.
You can place these Specifications in your domain packages, so anybody using your domain package has access to them. Using a specification, you can bundle your business rules with your entities, without creating difficult-to-read entities with undesired dependencies on services and repositories. If needed, you can inject dependencies on services or repositories into a specification.
Depending on the context, you can build different validators using the specification objects.
Main concern of entities should be keeping track of business state - that's enough of a responsibility and they shouldn't be concerned with validation.
Example
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
Two specifications:
public class IdNotEmptySpecification : ISpecification<User>
{
public bool IsSatisfiedBy(User subject)
{
return !string.IsNullOrEmpty(subject.Id);
}
}
public class NameNotTakenSpecification : ISpecification<User>
{
// omitted code to set service; better use DI
private Service.IUserNameService UserNameService { get; set; }
public bool IsSatisfiedBy(User subject)
{
return UserNameService.NameIsAvailable(subject.Name);
}
}
And a validator:
public class UserPersistenceValidator : IValidator<User>
{
private readonly IList<ISpecification<User>> Rules =
new List<ISpecification<User>>
{
new IdNotEmptySpecification(),
new NameNotEmptySpecification(),
new NameNotTakenSpecification()
// and more ... better use DI to fill this list
};
public bool IsValid(User entity)
{
return BrokenRules(entity).Count() == 0;
}
public IEnumerable<string> BrokenRules(User entity)
{
return Rules.Where(rule => !rule.IsSatisfiedBy(entity))
.Select(rule => GetMessageForBrokenRule(rule));
}
// ...
}
For completeness, the interfaces:
public interface IValidator<T>
{
bool IsValid(T entity);
IEnumerable<string> BrokenRules(T entity);
}
public interface ISpecification<T>
{
bool IsSatisfiedBy(T subject);
}
Notes
I think Vijay Patel's earlier answer is in the right direction, but I feel it's a bit off. He suggests that the user entity depends on the specification, where I belief that this should be the other way around. This way, you can let the specification depend on services, repositories and context in general, without making your entity depend on them through a specification dependency.
References
A related question with a good answer with example: Validation in a Domain Driven Design.
Eric Evans describes the use of the specification pattern for validation, selection and object construction in chapter 9, pp 145.
This article on the specification pattern with an application in .Net might be of interest to you.
I would not recommend disallowing to change properties in entity, if it's a user input.
For example, if validation did not pass, you can still use the instance to display it in user interface with validation results, allowing user to correct the error.
Jimmy Nilsson in his "Applying Domain-Driven Design and Patterns" recommends to validate for a particular operation, not just for persisting. While an entity could be successfully persisted, the real validation occurs when an entity is about to change it's state, for example 'Ordered' state changes to 'Purchased'.
While creating, the instance must be valid-for-saving, which involves checking for uniqueness. It's different from valid-for-ordering, where not only uniqueness must be checked, but also, for example, creditability of a client, and availability at the store.
So, validation logic should not be invoked on a property assignments, it should be invoked upon aggregate level operations, whether they are persistent or not.
Edit: Judging from the other answers, the correct name for such a 'domain service' is specification. I've updated my answer to reflect this, including a more detailed code sample.
I'd go with option 3; create a domain service specification which encapsulates the actual logic that performs the validation. For example, the specification initially calls a repository, but you could replace it with a web service call at a later stage. Having all that logic behind an abstract specification will keep the overall design more flexible.
To prevent someone from editing the name without validating it, make the specification a required aspect of editing the name. You can achieve this by changing the API of your entity to something like this:
public class User
{
public string Name { get; private set; }
public void SetName(string name, ISpecification<User, string> specification)
{
// Insert basic null validation here.
if (!specification.IsSatisfiedBy(this, name))
{
// Throw some validation exception.
}
this.Name = name;
}
}
public interface ISpecification<TType, TValue>
{
bool IsSatisfiedBy(TType obj, TValue value);
}
public class UniqueUserNameSpecification : ISpecification<User, string>
{
private IUserRepository repository;
public UniqueUserNameSpecification(IUserRepository repository)
{
this.repository = repository;
}
public bool IsSatisfiedBy(User obj, string value)
{
if (value == obj.Name)
{
return true;
}
// Use this.repository for further validation of the name.
}
}
Your calling code would look something like this:
var userRepository = IoC.Resolve<IUserRepository>();
var specification = new UniqueUserNameSpecification(userRepository);
user.SetName("John", specification);
And of course, you can mock ISpecification in your unit tests for easier testing.
I’m not an expert on DDD but I have asked myself the same questions and this is what I came up with:
Validation logic should normally go into the constructor/factory and setters. This way you guarantee that you always have valid domain objects. But if the validation involves database queries that impact your performance, an efficient implementation requires a different design.
(1) Injecting Entities: Injecting entities can be technical difficult and also makes managing application performance very hard due to the fragmentation of you database logic. Seemingly simple operations can now have an unexpectedly performance impact. It also makes it impossible to optimize your domain object for operations on groups of the same kind of entities, you no longer can write a single group query, and instead you always have individual queries for each entity.
(2) Injecting repository: You should not put any business logic in repositories. Keep repositories simple and focused. They should act as if they were collections and only contain logic for adding, removing and finding objects (some even spinoff the find methods to other objects).
(3) Domain service This seems the most logical place to handle the validation that requires database querying. A good implementation would make the constructor/factory and setters involved package private, so that the entities can only be created / modified with the domain service.
I would use a Specification to encapsulate the rule. You can then call when the UserName property is updated (or from anywhere else that might need it):
public class UniqueUserNameSpecification : ISpecification
{
public bool IsSatisifiedBy(User user)
{
// Check if the username is unique here
}
}
public class User
{
string _Name;
UniqueUserNameSpecification _UniqueUserNameSpecification; // You decide how this is injected
public string Name
{
get { return _Name; }
set
{
if (_UniqueUserNameSpecification.IsSatisifiedBy(this))
{
_Name = value;
}
else
{
// Execute your custom warning here
}
}
}
}
It won't matter if another developer tries to modify User.Name directly, because the rule will always execute.
Find out more here
In my CQRS Framework, every Command Handler class also contains a ValidateCommand method, which then calls the appropriate business/validation logic in the Domain (mostly implemented as Entity methods or Entity static methods).
So the caller would do like so:
if (cmdService.ValidateCommand(myCommand) == ValidationResult.OK)
{
// Now we can assume there will be no business reason to reject
// the command
cmdService.ExecuteCommand(myCommand); // Async
}
Every specialized Command Handler contains the wrapper logic, for instance:
public ValidationResult ValidateCommand(MakeCustomerGold command)
{
var result = new ValidationResult();
if (Customer.CanMakeGold(command.CustomerId))
{
// "OK" logic here
} else {
// "Not OK" logic here
}
}
The ExecuteCommand method of the command handler will then call the ValidateCommand() again, so even if the client didn't bother, nothing will happen in the Domain that is not supposed to.
in short you have 4 options:
IsValid method: transition an entity to a state (potentially invalid) and ask it to validate itself.
Validation in application services.
TryExecute pattern.
Execute / CanExecute pattern.
read more here
Create a method, for example, called IsUserNameValid() and make that accessible from everywhere. I would put it in the user service myself. Doing this will not limit you when future changes arise. It keeps the validation code in one place (implementation), and other code that depends on it will not have to change if the validation changes You may find that you need to call this from multiple places later on, such as the ui for visual indication without having to resort to exception handling. The service layer for correct operations, and the repository (cache, db, etc.) layer to ensure that stored items are valid.
I like option 3. Simplest implementation could look so:
public interface IUser
{
string Name { get; }
bool IsNew { get; }
}
public class User : IUser
{
public string Name { get; private set; }
public bool IsNew { get; private set; }
}
public class UserService : IUserService
{
public void ValidateUser(IUser user)
{
var repository = RepositoryFactory.GetUserRepository(); // use IoC if needed
if (user.IsNew && repository.UserExists(user.Name))
throw new ValidationException("Username already exists");
}
}
Create domain service
Or I can create domain service for
creating and editing users. But
someone can directly edit name of user
without calling that service...
If you properly designed your entities this should not be an issue.