A model .NET web service based on Domain Driven Design Part 3: the Domain

Introduction

In the previous post we laid the theoretical foundation for our domains. Now it’s finally time to see some code. In this post we’ll concentrate on the Domain layer and we’ll also start building the Infrastructure layer.

Infrastructure?

Often the word infrastructure is taken to mean the storage mechanism, like an SQL database, or the physical layers of a system, such as the servers. However, in this case we mean something different. The infrastructure layer is a place for all sorts of cross-cutting concerns and objects that can be used in any project. They are not specific to any single project or domain. Examples: logging, file operations, security, caching, helper classes for Date and String operations etc. Putting these in a separate infrastructure layer helps if you want to employ the same logging, caching etc. policy across all your projects. You could put these within the project solution but then when you start your next project then you may need to copy and paste a lot of code.

Infrastructure

The Entities we discussed in the previous post will all derive from an abstract EntityBase class. We could put it directly into the domain layer. However, think of this class as the base for all entities across all your DDD projects where you put all common functionality for your domains.

Create a new blank solution in VS and call it DDDSkeletonNET.Portal. Add a new C# class library called DDDSkeletonNET.Infrastructure.Common. Remove Class1 and add a new folder called Domain. In that folder add a new class called EntityBase:

public abstract class EntityBase<IdType>
{
	public IdType Id { get; set; }

	public override bool Equals(object entity)
	{
		return entity != null
		   && entity is EntityBase<IdType>
		   && this == (EntityBase<IdType>)entity;
	}

	public override int GetHashCode()
	{
		return this.Id.GetHashCode();
	}

	public static bool operator ==(EntityBase<IdType> entity1, EntityBase<IdType> entity2)
	{
		if ((object)entity1 == null && (object)entity2 == null)
		{
			return true;
		}

		if ((object)entity1 == null || (object)entity2 == null)
		{
			return false;
		}

		if (entity1.Id.ToString() == entity2.Id.ToString())
		{
			return true;
		}

		return false;
	}

	public static bool operator !=(EntityBase<IdType> entity1, EntityBase<IdType> entity2)
	{
		return (!(entity1 == entity2));
	}
}

We only have one property at this point, the ID, whose type can be specified through the IdType type parameter. Often this will be an integer or maybe a GUID or even some auto-generated string. The rest of the code takes care of comparison issues so that you can compare two entities with the ‘==’ operator or the Equals method. Recall that entityA == entityB if an only if their IDs are identical, hence the comparison being based on the ID property.

Domains need to validate themselves when insertions or updates are executed so let’s add the following abstract method to EntityBase.cs:

protected abstract void Validate();

We can describe our business rules in many ways but the simplest format is a description in words. Add a class called BusinessRule to the Domain folder:

public class BusinessRule
{
	private string _ruleDescription;

	public BusinessRule(string ruleDescription)
	{
		_ruleDescription = ruleDescription;
	}

	public String RuleDescription
	{
		get
		{
			return _ruleDescription;
		}
	}
}

Coming back to EntityBase.cs we’ll store the list of broken business rules in a private variable:

private List<BusinessRule> _brokenRules = new List<BusinessRule>();

This list represents all the business rules that haven’t been adhered to during the object composition: the total price is incorrect, the customer name is empty, the person’s age is negative etc., so it’s all the things that make the state of the object invalid. We don’t want to save objects in an invalid state in the data storage, so we definitely need validation.

Implementing entities will be able to add to this list through the following method:

protected void AddBrokenRule(BusinessRule businessRule)
{
	_brokenRules.Add(businessRule);
}

External code will collect all broken rules by calling this method:

public IEnumerable<BusinessRule> GetBrokenRules()
{
	_brokenRules.Clear();
	Validate();
	return _brokenRules;
}

We first clear the list so that we don’t return any previously stored broken rules. They may have been fixed by then. We then run the Validate method which is implemented in the concrete domain classes. The domain will fill up the list of broken rules in that implementation. The list is then returned. We’ll see in a later post on the application service layer how this method can be used from the outside.

We’re now ready to implement the first domain object. Add a new C# class library called DDDSkeleton.Portal.Domain to the solution. Let’s make this easy for us and create the most basic Customer domain. Add a new folder called Customer and in it a class called Customer which will derive from EntityBase.cs. The Domain project will need to reference the Infrastructure project. Let’s say the Customer will have an id of type integer. At first the class will look as follows:

public class Customer : EntityBase<int>
{
	protected override void Validate()
	{
		throw new NotImplementedException();
	}
}

We know from the domain expert that every Customer will have a name, so we add the following property:

public string Name { get; set; }

Our first business rule says that the customer name cannot be null or empty. We’ll store these rule descriptions in a separate file within the Customer folder:

public static class CustomerBusinessRule
{
	public static readonly BusinessRule CustomerNameRequired = new BusinessRule("A customer must have a name.");
}

We can now implement the Validate() method in the Customer domain:

protected override void Validate()
{
	if (string.IsNullOrEmpty(Name))
	{
		AddBrokenRule(CustomerBusinessRule.CustomerNameRequired);
	}
}

Let’s now see how value objects can be used in code. The domain expert says that every customer will have an address property. We decide that we don’t need to track Addresses the same way as Customers, i.e. we don’t need to set an ID on them. We’ll need a base class for value objects in the Domain folder of the Infrastructure layer:

public abstract class ValueObjectBase
{
	private List<BusinessRule> _brokenRules = new List<BusinessRule>();

	public ValueObjectBase()
	{
	}

	protected abstract void Validate();

	public void ThrowExceptionIfInvalid()
	{
		_brokenRules.Clear();
		Validate();
		if (_brokenRules.Count() > 0)
		{
			StringBuilder issues = new StringBuilder();
			foreach (BusinessRule businessRule in _brokenRules)
			{
				issues.AppendLine(businessRule.RuleDescription);
			}

			throw new ValueObjectIsInvalidException(issues.ToString());
		}
	}

	protected void AddBrokenRule(BusinessRule businessRule)
	{
		_brokenRules.Add(businessRule);
	}
}

…where ValueObjectIsInvalidException looks as follows:

public class ValueObjectIsInvalidException : Exception
{
     public ValueObjectIsInvalidException(string message)
            : base(message)
        {}
}

You’ll recognise the Validate and AddBrokenRule methods. Value objects can of course also have business rules that need to be enforced. In the Domain layer add a new folder called ValueObjects. Add a class called Address in that folder:

public class Address : ValueObjectBase
{
	protected override void Validate()
	{
		throw new NotImplementedException();
	}
}

Add the following properties to the class:

public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }

The domain expert says that every Address object must have a valid City property. We can follow the same structure we took above. Add the following class to the ValueObjects folder:

public static class ValueObjectBusinessRule
{
	public static readonly BusinessRule CityInAddressRequired = new BusinessRule("An address must have a city.");
}

We can now complete the Validate method of the Address object:

protected override void Validate()
{
	if (string.IsNullOrEmpty(City))
	{
		AddBrokenRule(ValueObjectBusinessRule.CityInAddressRequired);
	}
}

Let’s add this new Address property to Customer:

public Address CustomerAddress { get; set; }

We’ll include the value object validation in the Customer validation:

protected override void Validate()
{
	if (string.IsNullOrEmpty(Name))
	{
		AddBrokenRule(CustomerBusinessRule.CustomerNameRequired);
	}
	CustomerAddress.ThrowExceptionIfInvalid();
}

As the Address value object is a property of the Customer entity it’s practical to include its validation within this Validate method. The Customer object doesn’t need to concern itself with the exact rules of an Address object. Customer effectively tells Address to go and validate itself.

We’ll stop building our domain layer now. We could add several more domain objects but let’s keep things as simple as possible so that you will be able to view the whole system without having to track too many threads. We now have an example of an entity, a value object and a couple of basic validation rules. We have missed how aggregate roots play a role in code but we’ll come back to that in the next post. You probably want to see how the repository, service and UI layers can be wired together.

We’ll start building the repository layer in the next post.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

33 Responses to A model .NET web service based on Domain Driven Design Part 3: the Domain

  1. zyc says:

    Thank you for sharing this series,really help me a lot.

  2. You’re using a list of business rules to keep track of the failing business rules which means your entity can have an invalid state at a certain time.
    Wouldn’t you prefer preventing an invalid state of an entity at all time and throwing exceptions when properties are set to values which result in an invalid entity?

    • Andras Nemes says:

      Hello Stefaan,
      The Validate() method will make sure that invalid objects cannot be persisted which I think is the main purpose of business rules. The customer object will be in an invalid state for a very short time if incomplete parameters are provided by the client – between the creation and validation. I don’t think it does any harm.

      In case you need to pass around the object to other domains or domain services then it can be desirable not to let an invalid object fly around:

      public Customer(string name)
      {
      if name is null then throw exception;
      }

      //Andras

      • Ok, but why would you use business rules instead of enforcing validity on your entities at all time. I don’t seem to find any pros.

        Isn’t it the responsiblity of the entity to be persistable at all time. Is supose the Validate method has to be called from you repo’s before persisting thus leaving the possibility open where it wouldn’t be called?

  3. Velin says:

    Stefaan, yes I agree that domain objects should be persistable all the time, but .. practice and experience have shown that you organize your business logic flows in such way that you have some kind of ‘checkpoints’ where you validate (and persist) domain models. There you fire your validation. Also, you cannot always have full objects – for example you may fill your address ‘value’ objects in different steps in some workflow.
    @Andras, nice series, congrats to you for studious work.

  4. Jan says:

    Andras, this series is what I have been looking for for a long time now! Thanks for bringing it!

    I regard to the valueobject throwing an exception on validationerror I wonder why you chose this solution. Wouldn’t it be more natural to merge the businessrules of the root and all aggregates into one big list of validationerrors for the client to interpret?

    • Andras Nemes says:

      Jan, I see what you mean.
      Yes, it probably makes sense to present a list of the broken rules of the value objects as well that belong to the Entity. ValueObjectBase could also have a GetBrokenRules() method to return all broken rules just like the abstract EntityBase class.
      //Andras

  5. Jan says:

    Andras, what is your take on handling additions, changes and deletions from a list of related aggregates below the root (e.g. a list of customer preferences); how should this work in regard to the repository? Is it maintaining an internal list of the changes which is used during the persistence act (if so then how?), or should one just wipe the existing list in the datastore and create the entities again?

    • Andras Nemes says:

      Jan, check out the posts on SOA I mentioned in my previous reply to you. You’ll see an example there that sounds like the one you’re after in your question: handling a list of product purchases and product reservations. I took the “wipe out all” approach in the repository. It’s too cumbersome to check each product reservation line by line when there can be thousands of them, so they are first deleted and re-inserted.
      //Andras

  6. Johannes says:

    Andras,
    you choose to implement the validation of the business rules with the Validate method shown below.
    protected override void Validate()
    {
    if (string.IsNullOrEmpty(City))
    {
    AddBrokenRule(ValueObjectBusinessRule.CityInAddressRequired);
    }
    }
    How would you distinguish the context (use case) in this validate methods.
    For example:
    A first use case forces the customer to have an address in the usa.
    A second use case forces the customer to have AddressLine1 and AddressLine2 set.
    A third use case force the customer to have a postal code in sweden.
    …. and so on and so on.
    There are hundreds of case dependent business rules formulated by the customers.

    How would you solve this issue?
    Would you use a parameterized Validate method like this
    protected override void Validate(useCase)
    {
    if (useCase == useCase1)
    {
    AddBrokenRule(…);
    } else if (useCase == useCase2)
    {
    AddBrokenRule(…);
    } else if (useCase == useCase3)
    {
    AddBrokenRule(…);
    }
    }
    or some other approach?

    • Andras Nemes says:

      Hi Johannes,

      Having a 100 if-else statement doesn’t sound like a good idea in general. It makes testing and code maintenance a nightmare. I think those use cases need to be turned into proper objects that implement an abstraction. Then each customer must provide an implementation of that rule. The validate method then can simply call on the implemented rule which will contain the use-case specific rules.

      That way you can have a separate class for each use case that can be tested in isolation. You may want to read through the series on SOLID, especially the post on ‘L’ where a switch statement is replaced by objects that stand on their own.

      //Andras

      • Johannes says:

        Hi Andras,
        You stated:
        “I think those use cases need to be turned into proper objects that implement an abstraction. Then each customer must provide an implementation of that rule.”

        Please provide a concrete example because I have lots of ideas how to implement your suggestion but not all of them would be the intended solution.

        A first idea can be
        class CustomerUserCase1 : Customer
        class CustomerUserCase2 : Customer
        class CustomerUserCase3 : Customer
        which would work but leads to 100 classes (no good idead)

        A second idea can be
        class CustomerCreation {
        getCustomerForUserCase1() {
        c = new Customer()
        c.AddBrokenRules( all rules for use case 1)
        return c
        }
        getCustomerForUserCase2() {
        c = new Customer()
        c.AddBrokenRules( all rules for use case 2)
        return c
        }
        }
        which would lead to 100 methods. AddBrokenRules must be public. Every objection creation of a customer must use CustomerCreation, and so on.

        Thanks,
        Johannes

      • Andras Nemes says:

        Hi Johannes,

        I didn’t mean it quite that way. The goal is to elevate your rules to proper objects and forget 100 if-else cases and methods. If you have complicated rules by country then I believe you’ll have to create as many classes to represent those rules. I don’t mean that there should be 100 different forms of Customer objects – FrenchCustomer, GermanCustomer etc. – that would be baaaaad. Instead, concentrate on the rules around the customer. That way you can test each rule independently and group them using the decorator pattern if you can group common rules together.

        Let’s take an example similar to yours. I’ll keep it simple. Imagine your customers can order your products from 3 countries: England, France and Germany. Every customer has a first name, last name, email and an age. An overall rule is that every customer must have a first name regardless of the country. Furthermore:

        • French customers must be at least 18
        • German customers must have an email
        • English customers must have a last name

        A simplified scenario might consist of the following elements. All countries implement the following interface:

        public interface ICountry
        {
        	string Abbreviation { get; }
        }
        

        Here are the three concrete countries:

        public class Germany : ICountry
        {
        	public string Abbreviation
        	{
        		get { return "DE"; }
        	}
        }
        
        public class France : ICountry
        {
        	public string Abbreviation
        	{
        		get { return "FR"; }
        	}
        }
        
        public class England : ICountry
        {
        	public string Abbreviation
        	{
        		get { return "EN"; }
        	}
        }
        

        All country specific rules will derive from this base class:

        public abstract class CountrySpecificCustomerRule
        {		
        	public abstract ICountry Country { get; }
        	public abstract List<BusinessRule> GetBrokenRules(Customer customer);
        }
        

        The country specific rules are the following:

        public class EnglishCustomerRule : CountrySpecificCustomerRule
        {
        	public override ICountry Country
        	{
        		get { return new England(); }
        	}
        
        	public override List<BusinessRule> GetBrokenRules(Customer customer)
        	{
        		List<BusinessRule> brokenRules = new List<BusinessRule>();
        		if (string.IsNullOrEmpty(customer.LastName))
        		{
        			brokenRules.Add(new BusinessRule("English customers must have a last name"));
        		}
        		return brokenRules;
        	}
        }
        
        public class FrenchCustomerRule : CountrySpecificCustomerRule
        {
        	public override List<BusinessRule> GetBrokenRules(Customer customer)
        	{
        		List<BusinessRule> brokenRules = new List<BusinessRule>();
        		if (customer.Age < 18)
        		{
        			brokenRules.Add(new BusinessRule("French customers must be at least 18"));
        		}
        		return brokenRules;
        	}
        
        	public override ICountry Country
        	{
        		get { return new France(); }
        	}
        }
        
        public class GermanCustomerRule : CountrySpecificCustomerRule
        {		
        	public override List<BusinessRule> GetBrokenRules(Customer customer)
        	{
        		List<BusinessRule> brokenRules = new List<BusinessRule>();
        		if (string.IsNullOrEmpty(customer.Email))
        		{
        			brokenRules.Add(new BusinessRule("German customers must have an email"));
        		}
        		return brokenRules;
        	}
        
        	public override ICountry Country
        	{
        		get { return new Germany(); }
        	}
        }
        

        The country where the customer will be located is only known during runtime so the selection of the right country rule calls for a factory. Let’s take the simplest approach, i.e. a static factory:

        public class CountrySpecificCustomerRuleFactory
        {
        	private static IEnumerable<CountrySpecificCustomerRule> GetAllCountryRules()
        	{
        		List<CountrySpecificCustomerRule> implementingRules = new List<CountrySpecificCustomerRule>()
        		{
        			new EnglishCustomerRule()
        			, new FrenchCustomerRule()
        			, new GermanCustomerRule()
        		};
        
        		return implementingRules;
        	}
        
        	public static CountrySpecificCustomerRule Create(ICountry country)
        	{
        		return (from c in GetAllCountryRules() where c.Country.Abbreviation == country.Abbreviation select c).FirstOrDefault();
        	}
        }
        

        So we maintain the list of implementing rules in the factory and just return the correct one based on the country code.

        The customer must belong to a country. There are different ways to inject the country into the customer. Here I’ll take the simplest approach: constructor injection. The country specific rule will be selected using the factory. Here’s the Customer class:

        public class Customer
        {
        	private ICountry _country;
        
        	public Customer(ICountry country)
        	{
        		_country = country;
        	}
        
        	public string FirstName { get; set; }
        	public string LastName { get; set; }
        	public int Age { get; set; }
        	public string Email { get; set; }
        
        	public IEnumerable<BusinessRule> GetBrokenRules()
        	{
        		List<BusinessRule> brokenRules = new List<BusinessRule>();
        		//add overall broken rules
        		if (string.IsNullOrEmpty(FirstName))
        		{
        			brokenRules.Add(new BusinessRule("Customer must have a first name"));
        		}
        		//add country specific broken rules
        		brokenRules.AddRange(CountrySpecificCustomerRuleFactory.Create(_country).GetBrokenRules(this));
        		return brokenRules;
        	}
        }
        

        Test the code with:

        Customer frenchCustomer = new Customer(new France());			
        List<BusinessRule> brokenRules = frenchCustomer.GetBrokenRules().ToList();
        

        …which will yield 2 broken rules.

        If you have 100 different countries then you’ll need 100 [something] anyway, where a 100 if-else statements and a 100 methods in some large class are out of the question. At least I would not go down that path. I remember a similar case, a java project, where income tax forms had to be validated and the rules were country specific. There was one class per country at the end as that was the most suitable OOP solution out there.

        There are some other ways to inject the correct country into the customer, or organise the countries into a container and make them available by e.g. Countries.England – like Colors.Red in .NET graphics -, or using an abstract factory instead of a static one etc. The main point with my example is that those rules are recognised as “important” elements which should be organised into classes.

        Hope this helps,
        Andras

      • Johannes says:

        Hi Andras,
        thanks for your detailed explanation. That was exactly what I was looking for.

        Johannes

  7. bartek4c says:

    Great tutorial and great series. One question, quite naive probably, but why is EntityBase and ValueObjectBase classes located in the Infrastructure Project (the one holding cross-cutting concerns)? After all aren’t those classes relevant only to the domain project? Are they going to be used anywhere else?

    • Andras Nemes says:

      It’s not a naive question at all. The motivation to put them there is that the infrastructure layer can hold any classes and interfaces that are used throughout all your applications. The infra layer is meant to be reused and not dedicated to a single solution. Ideally if a company has multiple applications then they will all follow the same structure with domains, repos, services etc. The exact implementations will of course differ but if a developer working on project A has to suddenly work on project B then a similar structure will make it easier to get going. A common rule for all domain layers can be that they implement the same IAggregateRoot interface and derive from the same EntityBase superclass. Hence the developer will immediately feel at home. You’ll see similar elements in the other posts of the DDD series: e.g. IRepository is also part of the infra layer. It is normally only used in the Repository layer. It still makes sense, however, to put it in the infra layer so that ALL repository layers across ALL projects of the company will use that interface. This gives a degree of uniformity to the projects.
      //Andras

  8. Israel Pereira says:

    Hi Andras,

    One of my business rules is check if the username is unique.. So I need make a database check, How can I do this on Domain Layer ?

    • Andras Nemes says:

      Hi Israel,

      You don’t do that in the domain layer directly. Not sure how long you’ve got with the DDD series but this is one way:

      – extend IUserRepository in the Domain layer with a method that retrieves a User based on a user name
      – implement IUserRepository in the concrete repository layer
      – IUserService will then call upon IUserRepository to get a user with a user name
      – if that’s null then the user doesn’t exist otherwise throw an exception

      //Andras

      • Israel Pereira says:

        sorry, I think I didn’t express myself well..

        I know we need use the Repositories and Services, but I did not know how to do this.

        But now is ok, I got it, Thanks for the answer and for the DDD series.

  9. Yegor says:

    First of all, thank you for posting.

    I have agreed with every word in the intro parts, until I got to part 3.

    1. Regarding the Entity base class:
    1.1. The Id must be readonly as a class member and as property without setter.
    1.2. If you have some entities with Id generated after its creation, clearly you cannot use the same base for both.
    1.3. It is the responsibility of the Identity to decide whether it equals to other or not. The entity must not do that, clearly, it must not decide that Identity could be converted and compared as string.

    2. Validation – you have separated the data and the logic instead of keeping them together. The validation must happen the very moment a client tries to modify (call method, set property) an object. What if a client doesn’t call the validation or ignores its result?
    In case you allow an object to be in invalid state (which btw could be persisted for later processing), again, you cannot use common Entity base class for both.

    Common Entity base class could work in some cases, but it’s not the first thing to start with.
    The model should evolve.

  10. Burak TARHANLI says:

    I’d like to say thank you so much for this great articles. As a junior developer, this is like a course for me.

  11. Behnam says:

    Hi Andras,
    Thanks for the great presentation on DDD.

    Actually I know that domain should not be anemic which means we should try to have our business rules in domain. But when it comes to DDD is it true to assume that in two condition we need push logic’s to domain services, first when some business rules relate to other object and second when it needs DataBase operation for example check if username is duplicate.

    Anyway thanks again for all articles in donetcodr.

    • Andras Nemes says:

      Hi Behnam,

      1. “some business rules relate to other object”:

      Yes, that’s certainly an option. You can also create specialised domain objects if it makes sense for your business. You can check out the series on SOA starting here which shows how the Product domain can be incorporated into the ProductPurchase and ProductReservation objects in part 2.

      In practice the rule is probably more flexible, i.e. you don’t always need to create a domain service just because a domain, like Product needs to check a property on another domain, like Customer.

      2. “when it needs DataBase operation for example check if username is duplicate”

      You can put the actual uniqueness check in the domain, e.g. through a method that accepts a list of customer names. The actual retrieval of the customer names must of course happen outside in some repository but the name validation itself can be put within the domain.

      //Andras

  12. Joseph says:

    Hello, are the classes and logic to acquire the application settings and pass them into the application for consumption, part of the infrastructure layer?

    • Andras Nemes says:

      Hi again,

      The interface and its implementation(s) are part of the Infrastructure layer.

      “pass them into the application for consumption”: do you mean how the settings are passed into the consuming classes? The most straightforward way is by constructor injection, i.e. you pass the settings into the consuming class through a caller.

      //Andras

  13. Pingback: Architecture and patterns | Michael's Excerpts

  14. Felipe T says:

    I am refactoring some of my code to strongly implement ddd, and found your articles. Thanks for them, great work.

    I am wondering if is a good practice raise exceptions for simple validations. Wouldn’t be better to create some kind of “response” objects to handle these expected exceptions instead of using exceptions to control the flow of validation?

    • Andras Nemes says:

      Validation with exceptions is actually quite common, especially when checking for null values in a constructor. However, I can’t tell you whether it’s a “good” practice or not. Another user commented about collecting all the validation exceptions into a single collection of validation errors so that we can inform the user in the UI about all of them at the same time. That sounds like a good alternative.
      //Andras

  15. remus says:

    Hi Andras, this is the best DDD tutorial I’ve found on the web, nice and easy to understand and follow.

    One question though.
    You said in the 2nd post of the series, that Entities should be simple POCO no business rules attached to them. My confusion is because I see that you are attaching an abstract Validation method to BaseEntitie, thus any Entity which will derive from BaseEntity will have to provide implementation for it. Shouldn’t the Validations be in a different part?
    I don’t know maybe, use FluentValidation API and validate the Entity in another part let’s say a project called Domain.Entity.Validatiln ? so that the Entity would be as you stated as clean as possible?

    • Andras Nemes says:

      Hi, this is what I stated in part 2: ” Entities should ideally remain very clean POCO objects without any trace of technology-specific code”. Domain specific logic, including validation should be contained within domain objects as much as possible, not anywhere else. Technology-specific code is different from business rules. Technology specific code can be persistence with EntityFramework and the like. //Andras

  16. Jacky says:

    Hi Andras,
    Appreciate you provide the such great post about DDD, I’ve learned a lot stuff from it.
    I have a question, why should use ThrowExceptionIfInvalid(…) to verify the state of Address?
    How about if I make the Validate(…) of Address as public and invoke in Validate(…) of Customer?

Leave a Reply to bartek4c Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: