3

I have a company entity

public class Company : Entity<Company>
{
     public CompanyIdentifier Id { get; private set; }
     public string Name { get; private set; }
     ..............
     ..........
}

A company can be a agent or supplier or both or none. (There are more types) Its behaviour should be change based on types. Agent can get commission and supplier is able to invoice. What will be the best way to design the entity or entities or value objects? I have an option to add some boolean types and check those values inside methods,

 public class Company : Entity<Company>
    {
         public CompanyIdentifier Id { get; private set; }
         public string Name { get; private set; }
         public bool IsAgent { get; private set; }
         public bool IsSupplier { get; private set; }
         ..........

         public void Invoice()
        {
                if(!IsSupplier)
                {
                    throw exception.....;
                }
                //do something
        }

        public void GetCommission(int month)
        {
                if(!IsAgent)
                {
                    throw exception.....;
                }
                //do something
        }
         ..........
    }

To be honest, I do not like this. Is there any design pattern which might help to overcome this scenerio? What will you do and why to design this scenerio?

MJK
  • 3,434
  • 3
  • 32
  • 55

3 Answers3

1

I would look into separating the implementation for all those types in different classes. You could start doing this by using an enum to represent the company type.

public enum CompanyType
{
  Agent = 0,
  Supplier
}

public abstract class Company : Entity<Company>
{
   public CompanyIdentifier Id { get; private set; }
   public string Name { get; private set; }
   public CompanyType EntityType { get; private set; }

   public abstract void Invoice();
   public abstract void GetCommission(int month);
   ...

This way you get less public properties.

Next, I'd implement specialized classes for supplier and agent (and then for both and none). You can make Company abstract and any specialized methods abstract as well.

This will allow you to separate the distinct behaviors of each type of entity. Comes in handy when you get back to it for maintenance. It also makes the code easier read/understand.

public class SupplierCompany : Company
{
   public SupplierCompany()
   {
     EntityType = CompanyType.Supplier;
   }

   public override void Invoice()
   {...}
   public override void GetComission(int month)
   {...}
}

public class AgentCompany : Company
{
   public AgentCompany()
   {
     EntityType = EntityType.Agent;
   }

   public override void Invoice()
   {...}
   public override void GetComission(int month)
   {...}
}

With this you can eliminate testing for various types in methods like Invoice and GetComission.

Marcel N.
  • 13,726
  • 5
  • 47
  • 72
  • @MJK: You can if you add a third enum member `Both` and implement a third specialized class `SupplierAndAgentCompany`. Those classes can be really small and contain only specific code. All common code can go to the base class. – Marcel N. Aug 12 '14 at 16:30
  • @MJK: Plus, this way each class and focus on a single thing, not do a bunch of things (separation of concerns). – Marcel N. Aug 12 '14 at 16:30
  • 1
    I have many other types of company, such as operators, telephone customers, on-line customers and others. May be need to add more as it goes. If I started to create for all kinds of permutations then in future it will be very difficult to add any new types. – MJK Aug 12 '14 at 16:38
  • 1
    @MJK: I understand, but if you do it all in one class then you'll ultimately end up with a huge class. Even harder to add support for new types. – Marcel N. Aug 12 '14 at 16:40
  • 1
    @MJK: You can even go one step further and define the behaviors for each company through interfaces. Then maybe use a DI container to help you resolve specific companies more straightforward. – Marcel N. Aug 12 '14 at 16:43
1

Implement interfaces explicitly, then override the cast operator to only cast to that interface when valid.

public class Company : ...., IAgentCompany, ISupplierCompany ... {

    public double IAgentCompany.GetCommission(int month) {
            /*do stuff */
    }

    public static explicit operator IAgentCompany(Company c)  {
        if(!c.IsAgent)
            throw new InvalidOperationException();
        return this;
    }
}

Explicit implementations of interfaces must be called through their interface, not the concrete type:

// Will not compile
new Company().GetCommission(5);

// Will compile
((IAgentCompany)new Company()).GetCommission(5)

But, now we've overloaded the explicit cast operator. So what does that mean? We can't call GetCommission without casting to IAgentCompany, and now we have a guard to prevent that cast for a company that isn't marked as an agent.

Good things about this approach:

1) You have interfaces that define the aspects of different types of companies and what they can do. Interface segregation is a good thing, and makes the abilities/responsibilities of each type of company clear.

2) You've eliminated a check for every function you want to call that is not "global" to all companies. You do one check when you cast, and then as long as you have it in a variable typed as the interface, you can happily interact with it without any further checking. This means less places to introduce bugs, and less useless checks.

3) You are leveraging the languages features, and exploiting the type system to help make the code more bullet-proof.

4) You don't have to write tons of subclasses that implement the various combinations of interfaces (possibly 2^n subclasses!) with NotImplementedExceptions or InvalidOperationException everywhere in your code.

5) You don't have to use an enum or a "Type" field, especially when you are asking to mix and match these sets of abilities (you'd don't just need an enum, but a flag enum). Use the type system to represent different types and behaviors, not an enum.

6) It's DRY.

Bad things about this approach:

1) Explicit interface implementations and overriding explicit cast operators aren't exactly bread and butter C# coding knowledge, and may be confusing to those who come after you.

Edit:

Well, I answered too quickly without testing the idea, and this doesn't work for interfaces. However, see my other answer for another idea.

Clever Neologism
  • 1,322
  • 8
  • 9
  • I really like this idea. What should i do for some kind of action which is only valid for particular combination. For example, if the company is both agent and (any)customer are not allowed to get commission for its own purchase. This is just an example, there are many more behaviours depends on combination of types – MJK Aug 13 '14 at 07:59
  • I am playing with your approach and i end up with **"In c#, you are not allowed to use interfaces with the implicit or explicit operators"** [link](http://stackoverflow.com/questions/2433204/why-cant-i-use-interface-with-explicit-operator). @CleverNeologism: am i missing anything? – MJK Aug 13 '14 at 10:52
  • I have added another class AgentCompany which also implements IAgentCompany. Then the following cast operator to cast to agentcompany class works inside the Company class `code public static explicit operator AgentCompany(Company c) { if (!c._isAgent) throw new InvalidOperationException(); return ((AgentCompany)c); } ` @clever-neologism Is it okay or not? – MJK Aug 14 '14 at 07:49
  • You could do it that way, sure. In this case, the Company class implements the interface's definitions, but doesn't declare that it does, while its – Clever Neologism Aug 14 '14 at 14:10
  • Is it OK or not? Well, you'd need to test it against usage like you were using the library, but it looks right. In your example, the Company object would implement all the interfaces functions, but not declare that it does in the class statement, while the dummy child classes (`AgentCompany : Company` with no overrides) would. – Clever Neologism Aug 14 '14 at 14:12
0

As with most DDD questions, it usually boils down to Bounded Contexts. I'd guess you're dealing with some distinct bounded contexts here (this is most obvious from your statement "A company can be a agent or supplier or both or none."). In at least one context you need to consider all Company entities equally, regardless of whether they are Agents or Suppliers. However I think you need to think about whether or not your Invoice or GetCommission operations are applicable in this broader context? I'd say those will apply in more specialized contexts, where the distinction between an Agent and a Supplier is much more crucial.

You may be running into trouble because you're trying to create an all encompassing Company entity which is applicable in all contexts... this is almost impossible to achieve without weird code constructs & fighting against the type system (as is being suggested in your other answers).

Please read http://martinfowler.com/bliki/BoundedContext.html

As a rough idea of how your contexts might look:

Broad "Company" Context
{
    Entity Company
    {
        ID : CompanyIdentifier
        Name : String
    }
}

Specialized "Procurement" Context
{
    Entity Supplier
    {
        ID : CompanyIdentifier
        Name : String
        Invoice()
    }
}

Specialized "Sales" Context
{
    Entity Agent
    {
        ID : CompanyIdentifier
        Name : String
        GetComission()
    }
}

Does it make sense to try and use the same object in both Procurement and Sales contexts? These contexts have very different requirements after all. One of the lessons of DDD is that we split the domain into these bounded contexts, and do no try to make "God" objects which can do everything.

MattDavey
  • 8,897
  • 3
  • 31
  • 54
  • may be you are right for broader system. These types are basically some bunch of flags used mainly for one or two actions and reports. Is it a good idea to separating in multiple bounded contexts? And also some actions are only allowed for a combination, for example on-line and telephone customers are allowed to get vouchers. What do you think about the sol from @CleverNeologism? – MJK Aug 13 '14 at 07:53
  • *"These types are basically some bunch of flags used mainly for one or two actions and reports."* That's fine, one of the main lessons in DDD is knowing when *not* to use it. Certainly for simple CRUD applications DDD is not necessary. I suggest you remove the `domain-driven-design` tag from your question :) – MattDavey Aug 13 '14 at 09:13