Santhosh
Santhosh

Reputation: 6707

Data Containers vs Objects coupled with behaviour

I am writing a storage solution for a workflow hierarchy.

To simplify the picture I have 2 types of objects, a Workflow and a WorkflowStep.

Even though WorkflowStep comes hierarchically under the Workflow, the Workflow does not aggregate WorkflowStep because we view these objects as just data containers.

So this leaves me with the following classes:

 public class Workflow : Node {
    public string UID;
    public string parentID;
}

public class WorkflowStep : Node {
    public string UID;
    public string parentID;
}

public class WorkflowEngine {
    public void Store(Workflow wf) {
    }

    public void Store(WorkflowStep wfs) {
    }
}

The reasoning for not aggregating WorkflowStep inside Workflow (even though logically that fits) is that these objects are purely viewed as data containers and they may be subject to changes later on and we want to keep the storage of these objects decoupled from the objects themselves.

The other alternative of course would be to do something like this:

 public class Workflow : Node {
    public List<WorkflowStep> steps;
    public string UID;
    public string parentUID;

    public void Store() { }
}

public class WorkflowStep : Node {
    public string UID;
    public string parentID;

    public void Store() { }
}

What are the pros and cons of either approach? Is there any literature that talks about both the designs?

Upvotes: 0

Views: 342

Answers (1)

Abdul Munim
Abdul Munim

Reputation: 19217

Even though Workflow and WorkflowStep are both data containers but keeping these aside from hierarchical measures doesn't solve your decoupling issue.

It is more logical to keep WorkflowStep on hierarchy of Workflow and to get along with decoupling you must introduce IoC in this case.

The Beauty of IoC is that changing the definitions of WorkflowStep which is a list in Workflow class will be just transparent where you will only be considering to register your types on your IoC container.

Let me put you on an example with Ninject IoC container framework.

Define interfaces and implement your data containers accordingly:

public interface IWorkflow {
    string UID { get; set; }    
    string parentID { get; set; }
    IList<IWorkflowStep> Steps { get; set; }
}

public interface IWorkflowStep {
    string UID { get; set; }    
    string parentID { get; set; }
}

 public class Workflow : IWorkflow, Node {

    public string UID { get; set; };
    public string parentID { get; set; };
    public IList<IWorkflowStep> Steps { get; set; }
}

public class WorkflowStep : IWorkflowStep, Node {
    public string UID { get; set; };
    public string parentID { get; set; };
}

And now, the Ninject module be:

public class WorkflowModule : NinjectModule
{
    #region Overrides of NinjectModule

    public override void Load()
    {
        Bind<IWorkflow>().To<Workflow>();
        Bind<IWorkflowStep>().To<WorkflowStep>();
        Bind<IList>().To<List>();
    }

    #endregion
}

This is the single place where you bind your interfaces with concrete classes. And rest of the world, you just ask for an instance of defined interface.

To resolve your type, you need to create a Ninject Kernel which is an IKernel type and a concrete implementation of StandardKernel by loading your defined module.

Which is something like,

var kernel = new StandardKernel(new WorkflowModule());

Now, all you have to do is resolve your desired interface, like:

IWorkflow workflow = kernel.Get<IWorkflow>();
IWorkflowStep workflowStep = kernel.Get<IWorkflowStep>();

The beauty here is, you don't need to worry about your concrete implementation and which is very tightly coupled within your system. Its just the interface you will be dealing with and rest are the worries of your IoC container implementation.

As you are more worried about the implementation of WorkflowStep to be changed and not coupling with Workflow. I guess, this is where IoC comes to play.

Please be noted that, you can use any IoC container framework like Unity, Spring.NET, StructureMap and etc. I used Ninject because I am comfortable with it.

Upvotes: 1

Related Questions