Monday, December 17, 2012

Slicing Concerns, And Naming Them

Naming is hard.  Especially in OO.  To name something, you have to understand it at it's deepest level. You must capture it's true essence.  This is hard when you're giving a name to a thing that already exists, but it's orders of magnitude harder when you're simultaneously creating the thing out of thin air, and trying to decide what to call it.  Which is after all what we do when we're designing code.

The "essence of things" correlates closely with concepts like Separation of Concerns and the Single Responsibility Principle.  You can slice any object into ever smaller concerns or responsibilities.  You can slice it right down to it's constituent atoms!  Many design problems, like tight coupling and loss of flexibility, are in large part due to having concerns and responsibilities defined at too high a level.  Could this be so common simply because it's so hard to find names for the smaller concepts?  It's frequently easy to see what those separate concepts may be, but terribly hard to think what to name them!

Let's have an example:
This is entirely fictional code, but it's not so different from a lot of real code I've seen in the wild. And it illustrates this problem of slicing concerns very well.

At first glance, it seems very simple. The domain has a Task concept which has a default due date (set in the constructor), and which sends a notification email after it's inserted (using an ActiveRecord hook).  This very nicely and completely describes what a task is and how it behaves in our system.  And the names make it very intuitive.

Or do they?  Is it really the case that every single time we insert a task in the database it should send an email?  Unlikely.  We should slice that behavior out and put it somewhere else:
public class _WhatShouldThisBeCalled_
  public class _WhatShouldThisBeCalled_(Task t)
    Email.Send(t.AssignedTo_UserId, "New Task", "You have been assigned a new task");
This is an incredibly simple refactoring, but I have no idea what this class should be called. The method is a bit easier, it could be InsertAndNotify(Task t) or something similar. But what is this class? What concern does it represent?

No, really, I'm actually asking you.  What would you call it?

Or how else would you write it?  Maybe you'd do something like a fire an event and have someone hook it?  How would they hook it?  Maybe we need an EventAggregator?  This is getting awfully complex for such a simple requirement!

And it's not done, because it's not really so great that it defaults the DueOn date in the constructor.  Is every single task really due tomorrow?  Or is it just a certain kind of task, or tasks created in a certain way?  And where will we put that code, what will it be called?

I sincerely believe this is both a significant design problem, and a significant naming problem.  I want to know how you'd tackle it.  Please do leave a comment or tell me on twitter or even better, fork the gist on github!

These concerns need to be separate!  But what a cost we pay for it!  The simple OO domain model of a Task has turned into something much less relatable.  Either it's event driven spaghetti code with strange infrastructure objects like EventAggregators.  Or it's a hodge-podge of service or command classes, none of which actually model a relatable thing...  They only model functions, features, behaviors, use cases.  Or maybe we try applying inheritance, and then we end up in a whole different world of confusing names and surprising behaviors.

Can't we do better?  Is there some way we can do the slicing of concerns we need but still maintain the modeling of real relatable things?  Even if that may require a different way of thinking or not using the design patterns that led us here (Active Record, in this case).

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.