This article details the system design considerations for an Ejar contract registration workflow, focusing on the request lifecycle from validation to registration. Key architectural decisions include designing a lean state machine and carefully ordering operations for financial integrity. It highlights the importance of clear data validation and traceable transactions in complex workflows.
Read original on Dev.to #architectureThe article presents the engineering decisions behind designing a workflow for registering lease contracts with Saudi Arabia's Ejar platform. The core challenge lies in managing the request lifecycle accurately, ensuring data integrity, and handling financial transactions while interacting with an external government system that strictly verifies identities and data.
A crucial architectural decision was to resist the temptation of creating numerous states for every minor nuance in the workflow. Instead, the design employs a minimal state machine with only four states. The principle guiding this decision is that a state should only exist if it dictates a difference in system behavior, not just a different description of the request's status.
State Machine Design Principle
Only introduce a new state if it fundamentally changes the system's operational logic or available actions. Each additional state adds complexity to queries, dashboards, and migrations.
The sequence of operations during contract request creation is critical, especially concerning financial transactions. The system prioritizes data validation *before* any money is debited. This prevents unnecessary charges for incomplete or incorrect data, which is essential when dealing with external, strict validation systems.
// 1. Validate completeness - before anything that costs money
const missingFields = this.validateContractData(contract);
if (missingFields.length > 0) {
throw new BadRequestException({
message: 'Contract data is incomplete for electronic registration',
missingFields,
});
}
// 2. Compute fees
// 3. Debit
// 4. Create the request
// 5. Back-link the wallet transaction to the request