Domanda

Any given entity in my domain model has several invariants that need be enforced -- a project's name must be at least 5 characters, a certain product must exist to be associated with the project, the due date must not be prior to the current date and time, etc.

Obviously I want the client to be able to display error messages related to validation, but I don't want to constantly maintain the validation rules between several different layers of the program -- for example, in the widget, the controller, the application service or command object, and the domain. Plus, it would seem that a descriptive error message is presentation-related and not belonging to the domain layer. How can I solve these dilemmas?

È stato utile?

Soluzione

I would create specific exceptions related to your expected error conditions. This is standard for Exception handling in general and will help with your issue. For example:

public class ProjectNameNotLongEnoughException : System.Exception

or

public class DueDatePriorToCurrentDateException : System.Exception

Mark these possible exceptions in the xml comments for the methods that may throw them so that applications written against your domain model will know to watch out for these exceptions and will be able to present a message within the presentation of the application. This also allows you to have localized error messages based on the culture without cluttering up your domain model with presentation concerns.

If you choose to perform client-side validation, I'm afraid that you can't have your cake and eat it too. In this case, you may have to duplicate validation logic in order to achieve the desired features while maintaining your architecture.

Hope this helps!

Altri suggerimenti

I realise this is an old question, but this may help others in a similar situation.

You have here Behavior and Conditions which you need to encapsulate into your domain model.

For example, the ProjectName having a requirement on a certain length I would suggest should be encapsulated within a ValueObject. It may seem overboard for some, but within our Domain Model we almost always encapsulate native types, especially String, within a ValueObject. This then allows you to roll your validation within the constructor of the ValueObject.

Within the Constructor you can throw an Exception relating to the violation of the parameters passed in. Here is an example of one of our ValueObjects for a ZoneName:

public ZoneName(string name)
{

    if (String.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentNullException("Zone Name is required");
    }

    if (name.Length > 33)
    {
        throw new ArgumentException("Zone name should be less than 33 characters long");
    }

    Name = name;
}

Now consumers of that ValueObject can either perform their own validation before calling the constructor, or not, but either way your invariants will be consistent with your model design.

One way we build validation rules within your Domain Model, and then utilise them within your UI is to use the Mediatr module, which uses a One Model In, One Model Out pattern, and allows you to define Validators for each of your Query or Command models. These are defined using FluentValidation. You can then add a Provider to the ModelValidatorProviders within MVC. Take a look at JBogards ContosoUniversity example here https://github.com/jbogard/ContosoUniversity/tree/master/src/ContosoUniversity and look at the DependancyResolution folder, DefaultRegistry.cs.

Your other example of a Product must exist to be linked to a Project. This sounds to me like a Domain Service would be the best option to facilitate the cooperation between 2 bounded contexts? The Domain Service will ensure the invariants remain consistent across the bounded contexts. That Domain Service would not be exposed publically, so you would need an ApplicationService or a CQRS type interface which will take that DomainService as a dependency, allowing the DomainService to perform the operations required. The DomainService should contain the Domain Behavior, whereas the Application Service should just be a facilitator to call that function. Your DomainService would then throw exceptions rather than result in inconsistent or invalid invariants.

You should ultimately end up in a position where you don't have duplicated validation, or at least you never end up with invalid invariants because validation has not been performed at some point, as validation is always handled within your domain model.

While a descriptive error message may seem to pertain to presentation moreso than business, the descriptive error message actually embodies a business rule contained within the domain model -- and when throwing an exception of any kind, it is best practice to pass along some descriptive message. This message can be re-thrown up the layers to ultimately display to the user.

Now, when it comes to preemptive validation (such as a widget allowing the user to only type certain characters or select from a certain range of options) an entity might contain some constants or methods that return a dynamically-produced regular expression which may be utilized by a view model and in turn implemented by the widget.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top