0

I have an object called StyleBundle.

public class StyleBundle
{
    public StylePricingType StylePricingType { get; private set;}
    public decimal Price {get; private set;}
    public IEnumerable<Style> Styles { get; set;}
    public DateTime StartDate {get; private set;}
    public TimeSpan Duration {get; private set;}
    public bool IsTransient {get; set;}

    public void ChangeStylePricingType(StylePricingType newStylePricingType)
    {
        this.StylePricingType = newStylePricingType;
    }

}

This StyleBundle object has a property called StylePricingType. The StylePricingType is an enum of two types:

PerStyle

Unlimited

The StylePricingType will effect the overall Price of the StyleBundle. The way it will affect the Price is by changing the Styles kept in the Styles list. An Unlimited StyleBundle will automatically include all available Styles, but a PerStyle StyleBundle will allow a user to manually pick which Styles they want to include.

I now need to allow the StylePricingType to be changed if the StyleBundle is transient (previous rules stated that once a StyleBundle is new'ed up, you can not change the StylePricingType).

BUT, in order to make this change, I need to run a check against the database via a repository/specification/service... aka, however I want to do it.

The check basically looks for any other StyleBundles during the same duration of the current StyleBundle, and makes sure there are no overlap in Styles in the StyleBundle.

Since changing this property on a transient StyleBundle requires a check against other persisted StyleBundles, what is the best way to go about implementing this?

  1. Use Constructor injection: inject a service into the StyleBundle entity's constructor. I don't like this, b/c I don't like injecting dependencies into my entities unless I need to do so. Also, since I don't like the idea of injecting the dependency into the constructor when it's only needed for the method call that will change the StylePricingType, I see this as bad design.

  2. Use Method injection: Since I would only need the service for this one method call, this seems to make more sense. Yet at the same time, I don't like the idea the user being able to change this type without knowing they're running a db query. Also, I'm still injecting a service into my entity, just in a different way, and I really do not like injecting anything into my entities.

  3. Use a Domain Service: this seems to be the most explicit of all. I could create a StyleBundleService class that has a ChangeStylePricingType method that uses a repository or specification to run the check given a StyleBundle. This way, the requirement is made very explicit in the code, but the drawback here is code could still call the ChangeStylePricingType method directly on the StyleBundle object, and BYPASS the ChangeStylePricingType method on the service I need to make. Even if I set the StylePricingType to get;set; instead of private set; and got rid of the ChangeStylePricingType method on StyleBundle, code could still make the change, bypassing the domain service.

So, these all seem like legitimate ways to go about doing something like this, so what is the best/most accepted way of doing it using DDD? Also, maybe my StyleBundle object is trying to do too much, and should be broken into smaller classes/functionality that would allow this requirement change to be handled more eloquently?

Mike

Michael McCarthy
  • 1,502
  • 3
  • 18
  • 45
  • There's a couple of things I don't quite get here : - Why should the property be called Style*Pricing*Type ? It seems to affect the way styles can be added to the bundle much more than the price itself, which is only inferred from the styles contained in the bundle, right ? - You say that changing the StylePricingType should check for overlaps with other bundles, but manually adding styles in PerStyle mode should also perform some similar check, shouldn't it ? How do you handle that in the first place ? – guillaume31 Jan 12 '12 at 12:48
  • It feels like something is missing, maybe another concept in your domain/ubiquitous language that would allow you to take care of adding styles. Why not delegate the addition of Styles to a Strategy (or Policy) that would be either Unlimited or PerStyle ? – guillaume31 Jan 12 '12 at 12:51
  • ian31, both great observations. As far as how I handle the check for adding Styles regardless of type... I currently call a Specification and pass in the StyleBundle to it. The spec basically tells me if if the StyleBundle passed in with the Styles in contains is allowed. Again, I'd like to use a Domain Service for this, or maybe double dispatch: [http://lostechies.com/jimmybogard/2010/03/30/strengthening-your-domain-the-double-dispatch-pattern/](http://lostechies.com/jimmybogard/2010/03/30/strengthening-your-domain-the-double-dispatch-pattern/). That would be more explict. – Michael McCarthy Jan 12 '12 at 20:03
  • or maybe use the State pattern to represent the differnt behavior of a StyleBundle if it's in an "Unlimited" state or a "PerStyle" state... although, I feel like that might be shoe-horning the State pattern into this currently problem. – Michael McCarthy Jan 12 '12 at 20:07

2 Answers2

0

This is a common issue encountered in DDD. A similar problem is discussed by Udi Dahan in this post. Option 1 is discussed in this question. Option 2 is discussed elsewhere on SO (don't have exact link), but like you, I am not a fan, even though it is the simplest and most direct way. Option 3 is often associated with an anemic domain model, however I often find it to be preferable. The reason is that an encapsulating service layer is something that arises natural as part of DDD - it exposes the domain layer to other layers, such as the presentation layer, or an open host service. Furthermore, actions performed on domain entities can be represented as command objects which are handled by the service. In this case, you can have:

class ChangeStylePricingTypeCommand {
  public string StyleBundleId { get; set; }
  public StylePricingType StylePricingType { get; set; }
}

class StyleBundleService {
 IStyleBundleRepository db;

 public void Process(ChangeStylePricingTypeCommand command) {
   using (var tx = this.db.BeginTransaction()) {
    var bundle = this.db.Get(command.StyleBundleId); 

    // verification goes here.

    bundle.ChangeStylePricingType(command.StylePricingType);

    this.db.Commit();
   }
 } 
}

The service StyleBundleService is a perfect place for accessing repositories and other services.

The approach outlined by Udi entails the ChangeStylePricingType raising a domain event, to which would be subscribed a handler, which in turn executes the required business logic. This approach is more decoupled, but is more complex and may be overkill. The other issue with a domain event based approach is that the handler executes after the event happened, and thus cannot prevent it, it can only deal with the consequences.

Community
  • 1
  • 1
eulerfx
  • 36,769
  • 7
  • 61
  • 83
0

While I agree it is a good idea to externalize that behavior out of StyleBundle, I usually try to avoid using Services as much as possible. To be more precise, I try to avoid naming something a Service if there are known pattern names that better suit what you really want the object to do.

In your example, it's still unclear to me whether you simply want to check the validity of a StyleBundle against the new StylePricingType you assign to it, rejecting the operation altogether if the bundle doesn't comply, or if you want to adjust the contents of the bundle according to the new StylePricingType.

In the first case a Specification seems best suited for that (you mentioned in the comments you're already using one when adding Styles to a bundle). In the second you need an object that will actually act on the Bundle, eliminating non-compliant styles. I'd use a StyleRuleOutStrategy/Policy with an Enforce() method taking the Bundle as a parameter. In both cases you'd call the relevant method of the new Specification/Strategy in the property setter when changing Specification/Strategy.

Note that the Strategy part takes all its meaning if the action to take is not the same when switching to PerStyle than when switching to Unlimited, but again from what you explained it is not clear this is the case.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • ian31, it's the second case. When the StylePricingType changes on a StyleBundle, the contents of the Styles will be changed. When changing to a StylePricingType of Unlimited, the currently Styles collection will be emptied, then filled with all avilable Styles in the system. When the StylePricingType is changed to PerStyle, it's up to the user to choose Styles to add to the StyleBundle. So, Unlimited is always "clear what's in the Styles collection and fill with all available Styles". Changing to PerStyle, you clear the colleciton, then let the user pick Styles to add to the StyleBundle. – Michael McCarthy Jan 13 '12 at 15:26