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
RequestFromCoordinatorfollow 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.,
ProfilOMGTComponentServicehandles 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:
- Extract Logic: Move validation rules into the Entity or a Domain Service (Specification Pattern).
- Segregate: Split the "Script" into:
- Command:
RequestProfileCommand(Data only). - Handler:
RequestProfileHandler(Execution logic).
- Command:
- Encapsulate: The Handler should orchestrate domain objects, not do the work itself.