-
Notifications
You must be signed in to change notification settings - Fork 30
Separating Concerns
Most challenges of IT programming isn't so much about math & algorithms, it's about managing complexity. Creating fluent workflows is great, but having fluent workflows sprinkled throughout your code leads to unnecessary duplication. Objectflow provides some tools to modularize your workflow logic. The keystone of this abstraction is IWorkflowMediator
that implements the mediator pattern. The other part of the abstraction is IStatefulObject.
When you descend from the abstract class WorkflowMediator<T>
you must provide an implementation for protected IStatefulWorkflow<T> Define()
. This is the central point where you plug in everything that you learned from the Quickstart Guide.
public class SiteVisitWorkflow : WorkflowMediator<SiteVisit> {
private IUserContext _context;
// We need this dependency for enforcing transition security
public SiteVisitWorkflow(IUserContext context) {
_context = context;
}
/// <summary>
/// This is where the workflow is defined. The mediator takes care of the
/// details about caching the definition, so consider this final.
/// </summary>
protected override IStatefulWorkflow<SiteVisit> Define() {
var wf = new StatefulWorkflow<SiteVisit>("Site Visit workflow")
.Yield("Created")
// Parameterized workflow step
.Do(ScheduleVisit)
.Yield("Scheduled")
.Do(x => x.Open())
.Yield("Opened")
.Do(PrepareForApproval)
.Yield("Pending")
.Do(x => x.Approve());
return wf;
}
/// <summary>
/// This is where security is implemented
/// </summary>
public override bool CanDoTransition(object from, object to) {
if (from == "Pending")
return _context.IsManager;
else
return base.CanDoTransition(from, to);
}
private void ScheduleVisit(SiteVisit visit, DateTime day) {
visit.ScheduledDate = day;
}
private void PrepareForApproval(SiteVisit visit) {
// some logic
}
}
By organizing our code into a mediator class, we abstract other code (like controllers as you can see below) from having to know anything about workflows. While workflows are simple on the surface, they quickly get bogged down with concepts like choices (approve/reject), security, error handling, and interacting with the UI.
Below is a sample MVC controller that interacts with the above workflow. Notice how little knowledge of the workflow is required to have this controller interact with it.
public class SiteVisitController : Controller {
private IWorkflowMediator<SiteVisit> _workflow;
private IRepository<SiteVisit> _repo;
public SiteVisitController(IWorkflowMediator<SiteVisit> workflow, IRepository<SiteVisit> repo) {
_workflow = workflow;
_repo = repo;
}
// Sometimes we need to pass a date
[HttpPost]
public ActionResult Submit(int siteVisitId, DateTime day) {
var visit = _repo.GetById(siteVisitId);
_workflow.Start(visit, day);
_repo.Save(visit);
return RespondTo(visit, _workflow.PossibleTransitions(visit).First());
}
// Most other times we just start the object
[HttpPost]
public ActionResult Submit(int siteVisitId) {
var visit = _repo.GetById(siteVisitId);
_workflow.Start(visit);
_repo.Save(visit);
return RespondTo(visit, _workflow.PossibleTransitions(visit).First());
}
private ActionResult RespondTo(SiteVisit visit, ITransition nextState) {
// logic
}
}