Skip to content

Commit 5c0eb06

Browse files
authored
feat(current-account): Add Starlark saga handlers and definitions for withdrawal/deposit (#672)
Extract step handlers from withdrawal and deposit orchestrators into a central registry-based pattern. Create Starlark DSL definitions (.star files) that document the saga workflows for future runtime execution. - Add saga_handlers.go with 7 registered domain handlers: - current_account.position_keeping.initiate_log - current_account.position_keeping.cancel_log - current_account.financial_accounting.initiate_booking_log - current_account.financial_accounting.capture_posting - current_account.financial_accounting.update_booking_log - current_account.financial_accounting.compensate_posting - current_account.repository.save - Add withdrawal.star defining 6-step withdrawal saga with compensation - Add deposit.star defining 6-step deposit saga with compensation Handlers extract dependencies from StarlarkContext and perform gRPC calls to PositionKeeping and FinancialAccounting services. All existing tests continue to pass. Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent c1537df commit 5c0eb06

3 files changed

Lines changed: 1097 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Deposit Saga Definition
2+
#
3+
# This Starlark script defines the deposit saga workflow for the Current Account service.
4+
# The saga executes a multi-step deposit operation with compensation on failure.
5+
#
6+
# Steps (executed sequentially):
7+
# 1. log_position: Create CREDIT entry in PositionKeeping service
8+
# 2. initiate_booking_log: Create booking log in FinancialAccounting service
9+
# 3. capture_debit_posting: Post DEBIT to clearing account (double-entry source)
10+
# 4. capture_credit_posting: Post CREDIT to customer account
11+
# 5. finalize_booking_log: Transition booking log to POSTED
12+
# 6. save_account: Persist account metadata
13+
#
14+
# Compensation Order (LIFO - Last In, First Out):
15+
# Failures trigger compensation of completed steps in reverse order.
16+
#
17+
# Input data (provided via input_data dictionary):
18+
# - account_id: string - Account identifier
19+
# - account_identification: string - Account identification for external services
20+
# - amount: string - Decimal amount as string (e.g., "100.50")
21+
# - currency: string - Currency code (e.g., "GBP")
22+
# - transaction_id: string - Unique transaction identifier
23+
# - clearing_account_id: string - Clearing account for double-entry (optional)
24+
25+
# Define the deposit saga
26+
deposit_saga = saga(name="current_account_deposit")
27+
28+
# Extract input data
29+
account_id = input_data["account_id"]
30+
account_identification = input_data["account_identification"]
31+
amount = Decimal(input_data["amount"])
32+
currency = input_data["currency"]
33+
transaction_id = input_data["transaction_id"]
34+
clearing_account_id = input_data.get("clearing_account_id", "")
35+
36+
# Step 1: Log position in PositionKeeping service with CREDIT direction
37+
log_position_step = step(name="log_position")
38+
log_position_result = invoke_handler(
39+
handler="current_account.position_keeping.initiate_log",
40+
params={
41+
"account_id": account_identification,
42+
"amount": amount,
43+
"currency": currency,
44+
"direction": "CREDIT",
45+
"transaction_id": transaction_id,
46+
},
47+
compensate_handler="current_account.position_keeping.cancel_log",
48+
compensate_params={
49+
"log_id": "${log_position_result.log_id}",
50+
"version": "${log_position_result.version}",
51+
"transaction_id": transaction_id,
52+
"account_id": account_identification,
53+
"direction": "CREDIT",
54+
},
55+
)
56+
57+
# Step 2: Initiate booking log in FinancialAccounting service
58+
initiate_booking_step = step(name="initiate_booking_log")
59+
booking_log_result = invoke_handler(
60+
handler="current_account.financial_accounting.initiate_booking_log",
61+
params={
62+
"account_id": account_id,
63+
"currency": currency,
64+
"transaction_id": transaction_id,
65+
"transaction_type": "DEPOSIT",
66+
},
67+
)
68+
69+
# Step 3: Capture DEBIT posting to clearing account (if double-entry enabled)
70+
# For deposits: DEBIT the clearing account (where funds come from)
71+
if clearing_account_id != "":
72+
debit_posting_step = step(name="capture_debit_posting")
73+
debit_result = invoke_handler(
74+
handler="current_account.financial_accounting.capture_posting",
75+
params={
76+
"booking_log_id": "${booking_log_result.booking_log_id}",
77+
"account_id": clearing_account_id,
78+
"amount": amount,
79+
"currency": currency,
80+
"direction": "DEBIT",
81+
"transaction_id": transaction_id,
82+
"posting_type": "debit",
83+
},
84+
compensate_handler="current_account.financial_accounting.compensate_posting",
85+
compensate_params={
86+
"booking_log_id": "${booking_log_result.booking_log_id}",
87+
"account_id": clearing_account_id,
88+
"amount": amount,
89+
"currency": currency,
90+
"direction": "CREDIT", # Reverse direction for compensation
91+
"transaction_id": transaction_id,
92+
"posting_type": "debit",
93+
},
94+
)
95+
96+
# Step 4: Capture CREDIT posting to customer account
97+
credit_posting_step = step(name="capture_credit_posting")
98+
credit_result = invoke_handler(
99+
handler="current_account.financial_accounting.capture_posting",
100+
params={
101+
"booking_log_id": "${booking_log_result.booking_log_id}",
102+
"account_id": account_id,
103+
"amount": amount,
104+
"currency": currency,
105+
"direction": "CREDIT",
106+
"transaction_id": transaction_id,
107+
"posting_type": "credit",
108+
},
109+
compensate_handler="current_account.financial_accounting.compensate_posting",
110+
compensate_params={
111+
"booking_log_id": "${booking_log_result.booking_log_id}",
112+
"account_id": account_id,
113+
"amount": amount,
114+
"currency": currency,
115+
"direction": "DEBIT", # Reverse direction for compensation
116+
"transaction_id": transaction_id,
117+
"posting_type": "credit",
118+
},
119+
)
120+
121+
# Step 5: Finalize booking log (transition to POSTED)
122+
finalize_booking_step = step(name="finalize_booking_log")
123+
finalize_result = invoke_handler(
124+
handler="current_account.financial_accounting.update_booking_log",
125+
params={
126+
"booking_log_id": "${booking_log_result.booking_log_id}",
127+
"status": "POSTED",
128+
},
129+
)
130+
131+
# Step 6: Save account metadata
132+
save_account_step = step(name="save_account")
133+
save_result = invoke_handler(
134+
handler="current_account.repository.save",
135+
params={
136+
"account_id": account_id,
137+
"transaction_id": transaction_id,
138+
},
139+
)
140+
141+
# Output the saga result
142+
result = {
143+
"status": "COMPLETED",
144+
"transaction_id": transaction_id,
145+
"position_log_id": log_position_result["log_id"],
146+
"booking_log_id": booking_log_result["booking_log_id"],
147+
"credit_posting_id": credit_result["posting_id"],
148+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Withdrawal Saga Definition
2+
#
3+
# This Starlark script defines the withdrawal saga workflow for the Current Account service.
4+
# The saga executes a multi-step withdrawal operation with compensation on failure.
5+
#
6+
# Steps (executed sequentially):
7+
# 1. log_position: Create DEBIT entry in PositionKeeping service
8+
# 2. initiate_booking_log: Create booking log in FinancialAccounting service
9+
# 3. capture_debit_posting: Post DEBIT to customer account
10+
# 4. capture_credit_posting: Post CREDIT to clearing account (double-entry)
11+
# 5. finalize_booking_log: Transition booking log to POSTED
12+
# 6. save_account: Persist account metadata
13+
#
14+
# Compensation Order (LIFO - Last In, First Out):
15+
# Failures trigger compensation of completed steps in reverse order.
16+
#
17+
# Input data (provided via input_data dictionary):
18+
# - account_id: string - Account identifier
19+
# - account_identification: string - Account identification for external services
20+
# - amount: string - Decimal amount as string (e.g., "100.50")
21+
# - currency: string - Currency code (e.g., "GBP")
22+
# - transaction_id: string - Unique transaction identifier
23+
# - clearing_account_id: string - Clearing account for double-entry (optional)
24+
25+
# Define the withdrawal saga
26+
withdrawal_saga = saga(name="current_account_withdrawal")
27+
28+
# Extract input data
29+
account_id = input_data["account_id"]
30+
account_identification = input_data["account_identification"]
31+
amount = Decimal(input_data["amount"])
32+
currency = input_data["currency"]
33+
transaction_id = input_data["transaction_id"]
34+
clearing_account_id = input_data.get("clearing_account_id", "")
35+
36+
# Step 1: Log position in PositionKeeping service with DEBIT direction
37+
log_position_step = step(name="log_position")
38+
log_position_result = invoke_handler(
39+
handler="current_account.position_keeping.initiate_log",
40+
params={
41+
"account_id": account_identification,
42+
"amount": amount,
43+
"currency": currency,
44+
"direction": "DEBIT",
45+
"transaction_id": transaction_id,
46+
},
47+
compensate_handler="current_account.position_keeping.cancel_log",
48+
compensate_params={
49+
"log_id": "${log_position_result.log_id}",
50+
"version": "${log_position_result.version}",
51+
"transaction_id": transaction_id,
52+
"account_id": account_identification,
53+
"direction": "DEBIT",
54+
},
55+
)
56+
57+
# Step 2: Initiate booking log in FinancialAccounting service
58+
initiate_booking_step = step(name="initiate_booking_log")
59+
booking_log_result = invoke_handler(
60+
handler="current_account.financial_accounting.initiate_booking_log",
61+
params={
62+
"account_id": account_id,
63+
"currency": currency,
64+
"transaction_id": transaction_id,
65+
"transaction_type": "WITHDRAWAL",
66+
},
67+
)
68+
69+
# Step 3: Capture DEBIT posting to customer account
70+
debit_posting_step = step(name="capture_debit_posting")
71+
debit_result = invoke_handler(
72+
handler="current_account.financial_accounting.capture_posting",
73+
params={
74+
"booking_log_id": "${booking_log_result.booking_log_id}",
75+
"account_id": account_id,
76+
"amount": amount,
77+
"currency": currency,
78+
"direction": "DEBIT",
79+
"transaction_id": transaction_id,
80+
"posting_type": "debit",
81+
},
82+
compensate_handler="current_account.financial_accounting.compensate_posting",
83+
compensate_params={
84+
"booking_log_id": "${booking_log_result.booking_log_id}",
85+
"account_id": account_id,
86+
"amount": amount,
87+
"currency": currency,
88+
"direction": "CREDIT", # Reverse direction for compensation
89+
"transaction_id": transaction_id,
90+
"posting_type": "debit",
91+
},
92+
)
93+
94+
# Step 4: Capture CREDIT posting to clearing account (if double-entry enabled)
95+
if clearing_account_id != "":
96+
credit_posting_step = step(name="capture_credit_posting")
97+
credit_result = invoke_handler(
98+
handler="current_account.financial_accounting.capture_posting",
99+
params={
100+
"booking_log_id": "${booking_log_result.booking_log_id}",
101+
"account_id": clearing_account_id,
102+
"amount": amount,
103+
"currency": currency,
104+
"direction": "CREDIT",
105+
"transaction_id": transaction_id,
106+
"posting_type": "credit",
107+
},
108+
compensate_handler="current_account.financial_accounting.compensate_posting",
109+
compensate_params={
110+
"booking_log_id": "${booking_log_result.booking_log_id}",
111+
"account_id": clearing_account_id,
112+
"amount": amount,
113+
"currency": currency,
114+
"direction": "DEBIT", # Reverse direction for compensation
115+
"transaction_id": transaction_id,
116+
"posting_type": "credit",
117+
},
118+
)
119+
120+
# Step 5: Finalize booking log (transition to POSTED)
121+
finalize_booking_step = step(name="finalize_booking_log")
122+
finalize_result = invoke_handler(
123+
handler="current_account.financial_accounting.update_booking_log",
124+
params={
125+
"booking_log_id": "${booking_log_result.booking_log_id}",
126+
"status": "POSTED",
127+
},
128+
)
129+
130+
# Step 6: Save account metadata
131+
save_account_step = step(name="save_account")
132+
save_result = invoke_handler(
133+
handler="current_account.repository.save",
134+
params={
135+
"account_id": account_id,
136+
"transaction_id": transaction_id,
137+
},
138+
)
139+
140+
# Output the saga result
141+
result = {
142+
"status": "COMPLETED",
143+
"transaction_id": transaction_id,
144+
"position_log_id": log_position_result["log_id"],
145+
"booking_log_id": booking_log_result["booking_log_id"],
146+
"debit_posting_id": debit_result["posting_id"],
147+
}

0 commit comments

Comments
 (0)