Skip to main content

Architectural Pattern: Transaction Script

1. Definition

The Transaction Script pattern organizes business logic into procedures that handle a single request from the presentation layer. Each procedure (script) contains all the logic necessary to validate the request, perform calculations, and persist data to the database.

"Organizes business logic by procedures where each procedure handles a single request from the presentation" — Martin Fowler

2. In WepNG Context: ComponentService

In the WepNG solution, this pattern is implemented via the ComponentService classes (inheriting from WepOmgtOrderProductComponentService or similar base classes).

Characteristics in Codebase

  • Procedural Flow: Methods like RequestFromCoordinator follow a top-to-bottom script: Check Access -> Get Data -> Mutate State -> Save -> Send Email.
  • Anemic Entities: The Domain Entities (e.g., ProfilOMGT) are primarily data containers (Getters/Setters) with little to no behavioral logic.
  • Explicit Dependencies: Services manually instantiate other services or helpers (e.g., CommunicableServiceFactory), creating high coupling.

Example

// EITWEP.Business.WepBusiness.OMGT.ProfilBiz.ProfilOMGTComponentService.cs

public EITServiceResponse<ProfilOMGT> RequestFromCoordinator(int profilId)
{
// 1. Context validation
var access = HasAccessToProfilOMGT(profilId);

// 2. Data manipulation
var profilRequest = new ProfilRequestOMGT { ... };
db.ProfilRequestOMGTs.Add(profilRequest);

// 3. Side effects (Email)
var emailService = ...;
emailService.Send(...);

// 4. Persistence
db.SaveChanges();
}

3. Analysis

Pros (Why it was used)

  • Simplicity: Easy to understand for simple CRUD operations.
  • Speed: Rapid development for independent features.
  • Transaction Boundary: The service method naturally defines the start and end of a DB transaction (via SaveChanges).

Cons (The current bottleneck)

  • Duplication: Business rules (e.g., "Can this profile be requested?") often get duplicated across different scripts (e.g., Backoffice vs Portal).
  • God Classes: Services tend to grow indefinitely as more features are added (e.g., ProfilOMGTComponentService handles Access, PDF, Email, and File I/O).
  • Testability: Hard to unit test because the logic is tightly coupled to the DB Context and HttpContext.

4. Evolution Strategy

We are moving away from monolithic Transaction Scripts towards a Rich Domain Model and CQS.

Target Pattern: Command Handlers

Instead of one giant Service, we split logic into small, focused Handlers.

Refactoring Path:

  1. Extract Logic: Move validation rules into the Entity or a Domain Service (Specification Pattern).
  2. Segregate: Split the "Script" into:
    • Command: RequestProfileCommand (Data only).
    • Handler: RequestProfileHandler (Execution logic).
  3. Encapsulate: The Handler should orchestrate domain objects, not do the work itself.