Tuesday, June 19, 2007

Domain Driven Design - What's in an Exception Name ?

Colin Jack does not feel that exception names should be part of the domain language. In one of my blog posts, he comments :
I also don't see the exceptions are part of the domain language, instead I see the associated rules as being part of the domain language. So for me the rule name should become part of the domain language but the exceptions do not have to be, its the rules that matter to me when discussing things with the domain experts.

Colin, I have to disagree with you. Here is an example from a real life project (as per your request in my blog comments) and a real discussion session with one of the domain experts.

Here is a snippet from a Capital Market Solutions codebase. The codebase is replete with domain specific terms, as it should be in a domain model implementation, starters (and unstarters) of the capital market domain are requested to look up relevant information for background of the terminologies .. apologies :-(

Class PositionManager is the domain abstraction for managing the Position of an account. Note that Position is the domain term which indicates the balance of the account.


class PositionManager {
  //..
  //..
  void updatePosition(Instrument ins, Date tradeDate,
    BigDecimal amount, TradeType tt, ..) {
    Position currentPosition = getPosition(acc, ins, tradeDate);
    currentPosition.doUpdate(amount, tt);
  }
  //..
}



Now have a look at the method Position#doUpdate invoked above. Here the update is done through a business rule modeled as a Strategy design pattern. The rule can have multiple implementations, hence modeled as an interface. Again we use domain driven principles here to isolate the execution of a process from the rules on which it operates. Note the checked exception which the method catches - ShortPositionException, we will come back to the name in just a moment. Keep reading ..

Here is the definition :


class Position {
  private PositionUpdationStrategy strategy;

  //..
  //..
  void doUpdate(BigDecimal amount, TradeType tt) {
    try {
      strategy.doUpdate(amount, tt);
    } catch (ShortPositionException sbex) {
      //.. handle
    }
  }
  //..
}



The PositionUpdationStrategy is the actual business rule for updating the position of an account and I show the default implementation of the rule.


interface PositionUpdationStrategy {
  void doUpdate(..) throws ShortPositionException;
}



The rule has been modeled purely using the domain language (aka the Ubiquitous Language) and is fairly intuitive to read even by a non-programmer domain guy. Just wear the hat of a domain guy and see if the following codebase sounds domainish.
If the trade is a buy-trade, add to the existing position. If the update results in a short-balance, throw an exception ShortPositionException.

I have highlighted the domain terms above to indicate that the rule implementation is a direct translation of what the domain guy speaks. Colin also agrees to the rule being part of the domain language. Now what about the exception which the rule throws ? We could have named it InsufficientBalanceException and in fact, we did it exactly that way only. Then I was going through a session with one of the domain experts, when he was trying to interpret the correctness of the business rule implementation looking at the codebase. He noted that the short-balance check should ideally raise an exception which indicates that it is related to a short-balance check - once again a part of the domain language. InsufficientBalanceException is too generic a term and should be narrowed to a more fitting domain terminology for the exception here. And thus came the ShortPositionException name, which is part of the Ubiquitous Language. Here is the snippet ..


class DefaultPositionUpdationStrategy
  implements PositionUpdationStrategy {
  //..
  //..

  void doUpdate(BigDecimal amount, TradeType tt)
    throws ShortPositionException {
    if (tt.isBuyTrade()) {
      // add to position
    } else if (isShortBalance(currentPosition, amount)) {
      throw new ShortPositionException(..);
    }
    // subtract from position
  }
}



Look at the interface for the rule, how the checked exception makes the contract much more domain friendly.

16 comments:

Flavio said...

I'll try to explain this in english. Sorry but I'm from Argentina and my english is not very good.

The situation of short balance have not to be modeled as an exception. Is a situacion that could happen in the domain. The exceptions are for exceptional things, like illegal states of objects, wrong parameters, etc. So, exceptions named domain oriented, for me, are smell.

Flavio said...

In addition...

The InsufficientBalanceException is an example that I use when I teach. That kind of exceptions drives to manage the domain logic inside the specific way of managing errors of the language.

An account with insufficient balance isn't an error, isn't an exception. Is a possibility inside the logic of the domain and have to be modeled like that. An account trying to do something with insufficient money mustn't break sequence with an exeption.

Unknown said...

I have to disagree with you Flavio, I think checked exceptions are the natural way to express conditions that, while unlikely to happen, have to be taken into account when invoking an operation.

Think of the InsufficientBalanceException... how would you let the caller know that there are insufficient funds if you don't throw an exception? Returning null or another flag? That would lead to using ifs to decide the action to take, which, in my opinion, is far uglier than cleanly writting the handler code inside a catch block.

Personally, I prefer using checked exceptions to model exceptional but expected flow of events, and unchecked exceptions for unrecoverable error conditions which the caller code can't do anything to handle.

My 2 cents.
Regards,
Pablo.

Flavio said...

Handle exceptional but expected situations inside a catch block never is "clean". Expected things are part of the business, are part of the main flow. In this case, the caller can ask first if the account has money, or can use the Specification pattern, or something else.

Unknown said...

Hi Pablo.

I think Flavio is right. When you model that kind of situation trhowing an exception, you are mixing the logic of what you want to do with the error logic. It's like a controlled goto. In addition... if you first ask if you can do something and then you do it, there's going to appear the method that is modeling the condition and you are going to capture that piece of knowledge for your domain model. You can use SpecialCase or Specification if you don't like ifs.

Regards.

Unknown said...

I am 100% +1 with Pablo. Modeling alternate flows of use cases with checked exceptions is a very much practiced idiom in Java. That is what checked exceptions are for! They make your api s robust and express the intent at a level which is much closer to the domain. In the example that I have cited, the domain expert actually told me that when they have a short-position for an account while processing a trade in the back-office, they flag it as an exceptional condition which needs to be treated in a special way. They need extra authorization from the supervisor to determine if the trade should go through considering other factors like collaterals etc.

Mikhail.Khludnev said...

Hello Debasish

First, I whant to tell you about my apprecation of your blog. It's greate! very informative and useful.

But, I can't agree with you about using checked exception in domain.

Yes, checked exception are made for "modeling alternate flows". But, Do you shure that it are required for this? I belive that "prevent throw by checking" technic (such we use in Iterator) is more suitable in DDD than amazing throwing/catching.

Unknown said...

Debasish:

you are confusing domain error flow with application error flow. What about the error flow of the error flow then?
If the domain expert told you that, then it's not an exceptional condition for you as modeler, it's the way they normally work.

Unknown said...

@Marcos:
In that case how do u plan to handle domain exceptions like ShortBalance during Position Updation? I would like the user to force take specific action when I hit this condition in course of the usecase. And my contract should reveal this - hence typed checked exceptions ..

Jing Xue said...

Debasish,
I agree with you that exceptions, being crucial part of any contract, should be considered part of the domain.
But when it gets to checked vs. unchecked, Here's why I have to disagree with you. :-)

In short, to me, a method throwing a checked exception is dictating its _immediate_ caller, which is kind of making things going backwards.

Cheers.
--
Jing

Unknown said...

I think basically it boils down to what philosophy are you using for exceptions.
I always frowned at using exceptions for control flow (and not sure of the performance implications).
In your example, you get a ShortPositionException because you're violating the contract. AFAIK if you follow DbC, the caller is responsible to ensure the position is not short before calling update if you're not buying.
On the other hand, if the domain speaks of exceptions, I understand trying to follow it as close as possible, but I'm not sure that plain Java is the right language to do it...

Unknown said...

@Jing:
I am not yet a belonger to any specific camp regarding checked exceptions in Java - I am still wondering .. I see that you are quite clear on your camp :-). In this current context, if I would like to force the caller of my api handle the alternate route of the use case as part of the business rule, what alternatives do I have other than checked exceptions ? This is where I vascillate - those typed api s where the client needs to be enforced into handling exceptional routes.

Anonymous said...

@Debasish
"I would like to force the caller of my api..." that's the philosophy I'm against really :-) - checked exception is just the expression of that philosophy on the surface.

And the reason is very simple - I can't find any software engineering doctrine that says the callee mandating the caller's logical flow is a good thing. Imagine a language where an API can explicitly define rules for its callers like "you must have an alternate flow to deal with my return value if it's 0,1,or 2." That would sound crazy, but it's essentially the same as checked exceptions in Java.

Other languages learned from Java's mistake - C# exceptions are all unchecked, for instance.

Cheers.
--
Jing

Unknown said...

@Jing:
I fully appreciate your point of view and the fact that many languages have learnt from Java's apparent mistake in checked exception design. This is the reason I mentioned in my earlier comment, that I am still looking for the proper camp. People who feel checked exceptions are evil say that it is not according to the doctrine of software engineering to force callers to handle exceptions. But, tell me, one thing - do you consider exceptions to be part of the api ? Or part of the contract ? I do. And hence I feel that callers should also honor the design of the contract and should be forced to handle the exception, which the designer considers as part of his api. Correct me, if I am wrong, but is this also not design by contract ?

Anonymous said...

@Debasish,

I absolutely agree with you that exceptions are part of the contract. However, IMHO, an API contract should _not_ dictate implementation - either way for that matter. That an exception is part of the contract only goes as far as saying "be _prepared_ to handle this exception," but not "you _must_ handle this exception right here right now."

Again let's use the return value analogy - it's perfectly fine for a contract to say "I might return null so don't be surprised when you see one," but it's rather excessive to say "you must have an if testing my return value and do something differently if it's null."

There are, again IMHO, actually two flaws in Java's exception design. The first one is the checked exceptions themselves, and the second one the complete compile-time ignoring of unchecked ones. Folks who feel checked exceptions are necessary are actually falling victims of the second flaw - there aren't any compile-time support for enforcing contracts including unchecked exceptions, therefore the overly prescriptive alternative must be used.

What Java could have done is to make all exceptions equal, and not to force any exception handling, but instead honor the "throws" declarations, and issue warnings on those aren't handled.

I also realize that you feel stronger urge to enforce exception handling in contracts than I do, because of another philosophical difference - I think things like "insufficient balance" aren't really exceptions, and should be expressed as the status of an account or a transaction in a first-class domain object.

But I guess that's an even finer line to draw, so, another debate for another day... :-)

Cheers.
--
Jing

Unknown said...

@Jing:
"Folks who feel checked exceptions are necessary are actually falling victims of the second flaw - there aren't any compile-time support for enforcing contracts including unchecked exceptions, therefore the overly prescriptive alternative must be used."


I agree to this observation. I have worked in large C++ projects where it was a real pain going through half-baked documentations to find out everything about exceptions. It is an irony that people only lament on the pains of checked exceptions, but totally ignore the pain that is caused by the lack of them. I have been through the second class of sufferings and I know what it takes.

Cheers.