3

I'm trying to use DDD in my current project (c#, mvc, nhibernate, castle) and I'm thinking of best way to check a constraint that says any field (or specific field) must not change if entity is in some state, ie. invoice that is booked (state=booked) must not have amount field changed. In service layer I get some DTO object (from gui or web service, etc) that I need to map to domain object. After mapping is complete I want to validate my object - in particular I want to check the specific constraint in my question.

Currently I'm thinking of:

  • tracking changes at entity level, i.e. on every setter add field to changed fields collection and switch NHibernate to use field access strategy. Variation of this pattern would be to throw exception on setter if changing value is not allowed
  • creating copy of object before mapping and the comparing original and mapped values
  • falling back on nhibernate and get this information from nhibernate session - however the rule will not be enforced at entity level (imho it breaks ddd)

What do you think about it? Do you know any good patterns for this? Or am I missing something and I need to change my way of thinking about this constraint?

Thanks in advance for help.

empi
  • 15,755
  • 8
  • 62
  • 78

3 Answers3

3

Domain objects in DDD are 'self validating'. In other words, it is not possible for client code to break domain rules because objects enforce their internal invariants. For example:

public class Invoice {
    private Money _amount;
    private InvoiceState _state;

    public void ChangeAmount(Money newAmount) {
        if(_state == State.Booked) {
            throw new InvalidOperationException(
                      "Unable to change amount for booked invoice.");
        }
        _amount = newAmount;
    }

    // Methods like this can be used by external code (UI) to check business
    // rules upfront, to avoid InvalidOperationException.
    public Boolean CanChangeAmount() {
        if(_state == State.Booked) {
            return false;
        }
        return true;
    }
}

Another example from DDD sample:

  public HandlingEvent(final Cargo cargo,
                       final Date completionTime,
                       final Date registrationTime,
                       final Type type,
                       final Location location,
                       final Voyage voyage) {

    ...

    if (type.prohibitsVoyage()) {
      throw new IllegalArgumentException(
                       "Voyage is not allowed with event type " + type);
    }

Never allow your UI framework treat domain object as dumb data container. Unfortunately this is encouraged by a lot examples on the internet and C#'s emphasis on getters and setters. If you change object state without enforcing business rules you will eventually end up with 'corrupted' objects. This is especially true for NHibernate because its Session 'remembers' all objects and will happily dump them into database on next commit or flush. But this is just a technicality, the main reason is that you need to be able to reason about Invoice related business rules just by looking at Invoice class. Also note that the code should be based on Ubiquitous Language. You should see words like 'Invoice', 'Booked', 'Amount' instead of generic 'Field', 'Property', 'Validator'.

UPDATE: empi, thank you for restating your problem. You might want to open a new question. This is the quote with my emphasis

As I said in one of my comments - this question is a part of a bigger problem I'm having. I'm looking for a standard way to define domain logic and constraints only in the domain and then translate it to gui and other layers. I don't see any common pattern to the following very common demand: the domain states that field cannot be edited and this rule is automatically converted to gui logic that disables the field and makes it readonly. I'm looking for sample solution in mvc stack. I feel like I'm reinventing the wheel and most developers just give up and duplicate the logic in gui

I think that you looking for a way to state everything in domain and then 'generate' UI. Something like Naked Objects for MVC? I never used this approach but I doubt that generated UI would ever win beauty or usability contest. In my opinion there will always be some 'restating' of business logic in UI. Some domain invariants are too complex, involve multiple fields, require repository and maybe even external services. I'm not sure that it is possible to generate high quality user interface automatically. I think that attempts to do this may start bending your model to conform to the UI infrastructure.

Dmitry
  • 17,078
  • 2
  • 44
  • 70
  • I have only one problem with this code - when this constraint is applied to many properties there will be a lot of similar code (that was the approach I was considering - including this logic on setter). – empi Aug 20 '11 at 16:53
  • About your edit - I completely agree with you. I want to set rules on domain objects. The rules will be then translated to gui and other layers, not the other way around. I'm currently looking for resources on how to do this. However, most examples are too vague and lack code examples. – empi Aug 20 '11 at 16:57
  • 1
    This is actually a bit misleading. In DDD, it's the *entire domain* that's self-validating, not necessarily every single object. Repositories are part of your domain, therefore it's perfectly acceptable for repositories to enforce the validation. Many implementations now use Data Annotations which don't actually validate immediately, they just tell other classes *how* to validate them. Aside from the method interception in my answer, another DDD-consistent method is to implement `INotifyPropertyChanging`, which the repositories would wire up and throw an exception if the object is frozen. – Aaronaught Aug 20 '11 at 17:47
  • @empi: I highly recommend you to look at Evan's DDD Sample: http://dddsample.sourceforge.net/. Its java but the concepts should be the same. In a way it is even better because the code does not depend on MS infrastructure that _sometimes_ encourages Anemic Model. – Dmitry Aug 20 '11 at 19:18
  • @Aaronaught: Yes, some domain invariants can not be enforced by Entity and this responsibility goes to Repository, Service or Specification. But I think that the Entity itself would be my _first_ candidate for this logic. There was an idea that you should try to keep data and associated behavior together. – Dmitry Aug 20 '11 at 19:29
  • Sure, but if it's cumbersome to do at the entity level (and it definitely is in this case) then there's nothing wrong with letting the repository inject it. – Aaronaught Aug 20 '11 at 19:31
  • @Dmitry: I checked java and .net port some time ago. However, I did not find answers to my question or maybe I didn't check the code good enough. Could you point me to a class in that domain model that implements something similar to what I'm looking for? – empi Aug 20 '11 at 19:37
  • @Aaronaught: I agree with you, this is pretty important and very often missed - an entity itself isn't guaranteed to be valid - it is only valid in context of an aggregate. The aggregate is validity and consistency unit. – kstaruch Aug 20 '11 at 19:41
  • As I said in one of my comments - this question is a part of a bigger problem I'm having. I'm looking for a standard way to define domain logic and constraints only in the domain and then translate it to gui and other layers. I don't see any common pattern to the following very common demand: the domain states that field cannot be edited and this rule is automatically converted to gui logic that disables the field and makes it readonly. I'm looking for sample solution in mvc stack. I feel like I'm reinventing the wheel and most developers just give up and duplicate the logic in gui. – empi Aug 20 '11 at 19:47
  • Concerning the CanXXX methods - sometimes rebuilding the aggregate is considered as very costly operation, in addition the time between UI retrieves entity and a user performs action on it can lead to stale data. What I tend to do is provide some of the queries (like the CanXXX) outside of the domain for exactly the same purpose as your CanXXX. Yes, this leads to slight duplication, but the performance (and clarity) gains are very often significant. – kstaruch Aug 20 '11 at 19:50
  • @empi: please see my updated response with a snipped from DDD sample and upate based on your comment. Might be a good idea to stop commenting on this thread. – Dmitry Aug 20 '11 at 20:15
1

When designing my domain objects, I try not to think of them just as a collection of data, but as an object that can be acted upon. Instead of providing direct access to the data (even through getter and setter methods), provide methods that correspond to the actions one might take with the object. Sometimes an action will change multiple data fields. Sometimes it might only change one and functionally be no different from a setter, but it is named such that it represents the action and not just a data modification.

With this approach you can easily enforce what actions would be allowed based on the state of the entity. For example with an Invoice, you might Add or Remove items. This would change the total, but access isn't provided to modify the total directly. When the invoice is in a certain state (e.g. booked) when you no longer allow changes, then enforce that by throwing an exception from the Add or Remove methods indicating the methods aren't valid in the current state. However other methods may still be valid such as those related to shipping or payment of the invoice.

Along with this approach, I've also used different entities to represent the same data at different points in the lifecycle. While the invoice is active, it needs to be an object that can be acted upon. However, once it reaches a final state, it is only used for viewing and reporting and none of the data changes. By using different entities (e.g. ActiveInvoice and CompletedInvoice), it becomes clear in the application where it is used as part of the process and where it is used just for viewing. It also makes it easier when dealing with archiving data that may be coming from a different table or read-only view.

If the object only has two states representing a mutable and non-mutable state without much logic for allowing different methods for various states, you can use Eric Lippert's 'Popsicle Immutability' pattern. It allows more direct modification of the object than, but then enforces its immutability once it is frozen.

g .
  • 8,110
  • 5
  • 38
  • 48
0

Although I can't find a good reference (I could swear I heard it from Martin Fowler several years ago, but a search of his site came up dry), I'm used to hearing this concept referred to as "freezing" or "freezable". It's normally used in combination with two-legged accounting transactions.

Specifically, an accounting transaction is not created until the corresponding item is frozen, at which point no actions are allowed to be taken on the item which could change the balance. In many cases, no further actions may be taken at all, except possibly for cancellation, which actually doesn't change the frozen item but simply results in a retroactive event being added.

Oddly, Microsoft implemented this in a completely different context with WPF. They use "freezable" primarily to indicate that change notifications are no longer necessary. If you are, in fact, using WPF, you might consider looking at the Freezable class.

Otherwise, if you want a truly generic pattern, I highly suggest you read through Kozmic's Dynamic Proxy Tutorial. Although it's mainly an excuse to show off the features of Castle Proxy, the "freezable" concept is exactly what he chooses to implement, and he shows a way to do this using a generic reusable library without having to write much additional code after the fact.

Although there is quite a lot of code to work out all the kinks, the very basic idea is to just write an interceptor and then create a proxy with it:

internal class FreezableInterceptor : IInterceptor, IFreezable
{
    private bool _isFrozen;

    public void Freeze()
    {
        _isFrozen = true;
    }

    public bool IsFrozen
    {
        get { return _isFrozen; }
    }

    public void Intercept(IInvocation invocation)
    {
        if (_isFrozen && invocation.Method.Name.StartsWith("set_",
            StringComparison.OrdinalIgnoreCase))
        {
            throw new ObjectFrozenException();
        }

        invocation.Proceed();
    }
}

public static TFreezable MakeFreezable<TFreezable>() 
  where TFreezable : class, new()
{
    return _generator.CreateClassProxy<TFreezable>(new FreezableInterceptor());
}

Note that the above is not production-quality code, it's just the intro. You should read more of the linked site for more information.

As far as I know, class/interface proxying is really the only way you can do this in a domain-agnostic way. Otherwise, you're going to have to re-implement the freezable logic for every freezable class - that is to say, putting a lot of if-then statements in your property setters and throwing an FrozenException if the status is set.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • I have a question - in your opinion, which layer should decide that the entity should be made freezable (service, repository)? Or maybe my repositories should always return proxies to my objects? – empi Aug 20 '11 at 17:18
  • @empi: You always freeze the actual model or transaction source (bill, payment, whatever). And yes, you would have your repositories always provide proxied (freezable/frozen) objects. You could add a "fail fast" protection by actually having the model classes implement `IFreezable` but by having the default (overridable) `Freeze` method throw a `NotSupportedException`, then use another interceptor to override it. That way if you ever accidentally end up with a non-proxied object which can't be frozen, you'll know pretty quickly. – Aaronaught Aug 20 '11 at 17:43
  • I would not favour this approach. The questions is related to DDD, and DDD is all about making the domain rules explicit. The rule is: "when the state is booked, you cannot change the amount". This suggested approach does not communicate that rule at all. Dmitry's answer is the one you want in my opinion. – David Masters Aug 22 '11 at 12:44