Reducing Coupling in a Series of Tasks

I am working on some code right now that involves processing user requests. Each request requires going through an approval. When a request is made, one or more records are created on the database recording what the request entails. Then a ticket is created and placed on the approver’s work queue.

For a typical request, approving it involves these steps:

  1. The request is processed (usually resulting in a table getting updated)
  2. The changes are recorded to a history table (record auditing).
  3. The request is marked as approved by the approver (approval auditing).
  4. The ticket is removed from the work queue.
  5. The original requester is sent an email telling them their request was approved.

We have a single class that kicks off these steps sequentially within a transaction (http://martinfowler.com/eaaCatalog/transactionScript.html). For the most part, the sequence doesn’t really matter and the email isn’t really essential.

The problem is each of these classes has at least 5+ dependencies (in reality, it’s closer to 10+). I am stumped about how to reduce the number of dependencies when it comes to a workflow like this.

I thought about grouping some of these items together, but there’s no logical grouping. I don’t see much of a point in creating an unnecessary layer like that, anyway.

I also thought about doing some kind of observer pattern (.NET events). A class higher up could listen for the event, instantiate the needed classes and kick them off. However, that “higher-up class” would then be dependent on all of the classes again, so I’d be right back in the same situation.

So, the only ways I’ve been able to think of reducing the number of dependencies is to either do artificial grouping of classes or to just make it the layer above’s problem. Neither seem ideal and I wonder if I am missing something. If it helps, I am using Ninject with ASP.NET MVC. I have access to an IOC container (DependencyResolver). I am wondering if the solution is some sort of combination between using listeners/events and an IOC container.

3

If you’re calling these steps, then clearly the order is relevant to some degree. The exact order may not matter, but there is going to be a certain amount of “X and Y happen before Z”. For example, you probably don’t want to send the email saying the request was approved, before it has actually been approved. Likewise, the request probably cannot be approved before it is actually processed/added to the queue/etc.

Transaction scripts (or “workflow managers”) are basically procedural code. The number of dependencies isn’t really the issue, it’s just a proxy for the issue. The actual issue is the number of responsibilities the script has. And in your case, the number of responsibilities grows with each new step you add. And that, of course, goes against the Single Responsibility Principle.

The most common antithesis to procedural code is event-driven code. You say this:

A class higher up could listen for the event, instantiate the needed classes and kick them off. However, that “higher-up class” would then be dependent on all of the classes again, so I’d be right back in the same situation.

But that’s missing the point. You’re still trying to have one class that’s responsible for every step. Instead, you want to have a class for each step – it listens for whatever precursor events are supposed to trigger that step, and then starts that step and that step alone. One responsibility, and very few dependencies, since the events themselves are just small bags of “arguments”, or in some cases just strings or marker interfaces.

There are lots of frameworks around to coordinate the event handlers, ranging from highly-reliable, transactional service buses like NServiceBus or Mass Transit, which are heavy on infrastructure and designed for distributed systems, all the way to Appcelerate (formerly bbvCommom/bbvEventBroker, which has a Ninject extension) or Udi Dahan’s simple Domain Events implementation, which are designed to run entirely in-process.

The basic idea is always the same, though:

  1. A client sends a message to initiate some process.
  2. A handler (or several) responds to that message.
  3. Each handler publishes some event whenever it is finished.
  4. Subsequent handlers may respond to those events and continue the process.

This goes on for as long as it has to, until there is no longer anything “listening”.

So, in your example, you might have these classes (note: this is not written to be specific to any particular framework, it’s more or less pseudocode):

// Messages
public class SubmitOrderCommand { ... }
public class ApproveOrderCommand { ... }
public class OrderProcessedEvent { ... }
public class OrderApprovedEvent { ... }

// Handlers
public class OrderProcessor : IConsume<SubmitOrderCommand>
{
    public void Handle(SubmitOrderCommand message)
    {
        // Process the order
        Events.Publish<OrderProcessedEvent>();
    }
}

public class OrderAuditor : IConsume<OrderCommand>, IConsume<OrderProcessedEvent>
{
    public void Handle(OrderCommand message) { // Write to audit log }
    public void Handle(OrderProcessedEvent message) { // Mark order as processed }
}

public class OrderApprover : IConsume<OrderProcessedEvent>, IConsume<ApproveOrderCommand>
{
    public void Handle(OrderProcessedEvent message)
    {
        // Notify somebody that an order needs to be approved
    }

    public void Handle(ApproveOrderCommand message)
    {
        // Remove ticket from work queue
        Events.Publish<OrderApprovedEvent>();
    }
}

public class OrderNotifier : IConsume<OrderApprovedEvent>
{
    public void Handle(OrderApprovedEvent message) { // Send an email }
}

I assume you get the idea. In this architecture, the number of dependencies for each handler is entirely limited to the actions that it is directly responsible for, and the one or two events that it consumes or publishes.

Of course, this means giving up the security blanket of having the entire workflow described in one place. Managers and business analysts really love their flow charts, and even programmers tend to get attached to the ability to see, at a glance, every step that’s involved in a particular process. The practical upside to the event-driven model is much looser coupling, testability, and the ability to expand the workflow “horizontally” without changing any existing handlers, or actually needing to understand or care about the exact order of events.

P.S. All of the frameworks I mentioned perform dependency injection on the handlers. You don’t instantiate the handlers, the framework does. That’s what makes this so different from a transaction script.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật