Skip to content

Commit a902fc3

Browse files
committed
docs: update mkdocs navigation structure for product and system documentation
1 parent a435aae commit a902fc3

File tree

3 files changed

+817
-115
lines changed

3 files changed

+817
-115
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# 🧭 Dhanman Messaging Design: Acknowledgment Event Pattern
2+
3+
## **Objective**
4+
5+
Enable asynchronous, reliable communication between services using **event-based acknowledgments** instead of blocking request–response.
6+
Each service that initiates a command will later receive a **targeted acknowledgment event** confirming completion and returning necessary identifiers.
7+
8+
---
9+
10+
## 🧩 **1. Design Philosophy**
11+
12+
- **Fire-and-Forget Commands:** Sales, Purchase, Payroll, and Community send commands to Common or other shared modules.
13+
- **Targeted Acknowledgment Events:** The receiver (e.g., Common) publishes back an acknowledgment event containing IDs or status for the initiating entity.
14+
- **Point-to-Point Routing:** Each acknowledgment event is delivered only to its respective source service, avoiding fanout noise.
15+
- **Guaranteed Delivery:** All acknowledgment events use durable queues bound to a **direct exchange** with explicit routing keys.
16+
17+
---
18+
19+
## 🔁 **2. High-Level Flow Example**
20+
21+
```mermaid
22+
sequenceDiagram
23+
participant Sales as Dhanman.Sales
24+
participant Common as Dhanman.Common
25+
26+
Sales->>Common: 🔶 CreateTransactionCommand (InvoiceId, Amount)
27+
Common->>CommonDB: 💾 Create Transaction
28+
Common-->>Sales: 🔵 TransactionPostedForInvoiceEvent (InvoiceId, TransactionId)
29+
Sales->>SalesDB: 🧾 Update Invoice (TransactionId, Posted = true)
30+
```
31+
32+
---
33+
34+
## 🧱 **3. Exchange and Queue Strategy**
35+
36+
| **Exchange** | **Type** | **Publisher** | **Routing Key Pattern** | **Consumer Service** | **Queue Example** |
37+
| ------------------- | ---------- | ------------- | ----------------------- | -------------------- | ------------------------------------------------------------------ |
38+
| `common.ack.events` | **direct** | Common | `invoice.posted` | Sales | `sales.invoice_posted.queue` |
39+
| `common.ack.events` | **direct** | Common | `bill.posted` | Purchase | `purchase.bill_posted.queue` |
40+
| `common.ack.events` | **direct** | Common | `salary.posted` | Payroll | `payroll.salary_posted.queue` |
41+
| `common.ack.events` | **direct** | Common | `customer.created` | Sales / Community | `sales.customer_created.queue`, `community.customer_created.queue` |
42+
| `common.ack.events` | **direct** | Common | `vendor.created` | Purchase | `purchase.vendor_created.queue` |
43+
| `common.ack.events` | **direct** | Common | `employee.created` | Payroll | `payroll.employee_created.queue` |
44+
45+
---
46+
47+
## 🧩 **4. Complete List of Acknowledgment Events**
48+
49+
| **Origin Service (Sender)** | **Target Publisher (Responder)** | **Command Sent** | **Acknowledgment Event** | **Payload Fields (Suggested)** | **Consumer Queue** |
50+
| --------------------------- | -------------------------------- | ---------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------- |
51+
| **Sales** | Common | `CreateBasicCustomerCommand` | `CustomerCreatedForSalesEvent` | `CustomerId`, `CompanyId`, `OrganizationId`, `CorrelationId`, `CreatedOnUtc` | `sales.customer_created.queue` |
52+
| | Common | `CreateTransactionCommand` | `TransactionPostedForInvoiceEvent` | `InvoiceId`, `TransactionId`, `CompanyId`, `OrganizationId`, `PostedOnUtc` | `sales.invoice_posted.queue` |
53+
| | Common | `CreateInvoicePaymentCommand` | `TransactionPostedForInvoicePaymentEvent` | `InvoicePaymentId`, `TransactionId`, `CompanyId`, `OrganizationId`, `PostedOnUtc` | `sales.invoice_payment_posted.queue` |
54+
| **Purchase** | Common | `CreateBasicVendorCommand` | `VendorCreatedForPurchaseEvent` | `VendorId`, `CompanyId`, `OrganizationId`, `CreatedOnUtc` | `purchase.vendor_created.queue` |
55+
| | Common | `CreateTransactionCommand` | `TransactionPostedForBillEvent` | `BillId`, `TransactionId`, `CompanyId`, `OrganizationId`, `PostedOnUtc` | `purchase.bill_posted.queue` |
56+
| | Common | `CreateBillPaymentCommand` | `TransactionPostedForBillPaymentEvent` | `BillPaymentId`, `TransactionId`, `CompanyId`, `OrganizationId`, `PostedOnUtc` | `purchase.bill_payment_posted.queue` |
57+
| **Payroll** | Common | `CreateBasicEmployeeCommand` | `EmployeeCreatedForPayrollEvent` | `EmployeeId`, `CompanyId`, `OrganizationId`, `CreatedOnUtc` | `payroll.employee_created.queue` |
58+
| | Common | `CreateTransactionCommand` | `TransactionPostedForSalaryEvent` | `SalaryId`, `TransactionId`, `CompanyId`, `OrganizationId`, `PostedOnUtc` | `payroll.salary_posted.queue` |
59+
| | Common | `CreateSalaryPaymentCommand` | `TransactionPostedForSalaryPaymentEvent` | `SalaryPaymentId`, `TransactionId`, `CompanyId`, `OrganizationId`, `PostedOnUtc` | `payroll.salary_payment_posted.queue` |
60+
| **Community** | Common | `CreateBasicCustomerCommand` | `CustomerCreatedForCommunityEvent` | `CustomerId`, `UnitId`, `CompanyId`, `OrganizationId`, `CreatedOnUtc` | `community.customer_created.queue` |
61+
| **Common (reverse ack)** | Community | (Triggered when Unit creates a Customer) | `CustomerLinkedToUnitEvent` | `UnitId`, `CustomerId`, `CompanyId`, `OrganizationId`, `LinkedOnUtc` | `community.customer_linked.queue` |
62+
63+
---
64+
65+
## ⚙️ **5. MassTransit Configuration Example**
66+
67+
### **Common.Api Publisher**
68+
69+
```csharp
70+
await _eventPublisher.PublishAsync(new TransactionPostedForInvoiceEvent
71+
{
72+
InvoiceId = command.InvoiceId,
73+
TransactionId = transaction.Id,
74+
CompanyId = command.CompanyId,
75+
OrganizationId = command.OrganizationId,
76+
PostedOnUtc = DateTime.UtcNow
77+
}, context =>
78+
{
79+
context.SetRoutingKey("invoice.posted");
80+
});
81+
```
82+
83+
### **Sales.Api Consumer**
84+
85+
```csharp
86+
cfg.ReceiveEndpoint("sales.invoice_posted.queue", e =>
87+
{
88+
e.ConfigureConsumeTopology = false;
89+
e.Bind("common.ack.events", x =>
90+
{
91+
x.RoutingKey = "invoice.posted";
92+
});
93+
e.ConfigureConsumer<TransactionPostedForInvoiceConsumer>(context);
94+
});
95+
```
96+
97+
---
98+
99+
## 🧠 **6. Why Use Direct Exchange**
100+
101+
✔ Only specific consumers receive relevant messages
102+
✔ Reduces unnecessary message traffic
103+
✔ Keeps event routing explicit and predictable
104+
✔ Easy to extend — just add a new routing key and queue binding for another event type
105+
106+
---
107+
108+
## 🪶 **7. Schema Example (Event DTO)**
109+
110+
```csharp
111+
public record TransactionPostedForInvoiceEvent(
112+
Guid InvoiceId,
113+
long TransactionId,
114+
Guid CompanyId,
115+
Guid OrganizationId,
116+
DateTime PostedOnUtc,
117+
Guid CorrelationId);
118+
```
119+
120+
All acknowledgment events follow a consistent convention:
121+
122+
- Include **CorrelationId** from originating command
123+
- Include **EntityId** (InvoiceId, BillId, etc.)
124+
- Include **TransactionId**
125+
- Include **OrganizationId**, **CompanyId**, and **Timestamp**
126+
127+
---
128+
129+
## 🧩 **8. Summary Checklist**
130+
131+
| ✅ Goal | 🧩 Implementation |
132+
| -------------------------------- | ------------------------------------------------------------------ |
133+
| Maintain async non-blocking flow | Fire commands, listen for acknowledgment events |
134+
| Event naming consistency | Use `TransactionPostedForXEvent`, `CustomerCreatedForXEvent`, etc. |
135+
| Routing isolation | Use direct exchange with service-specific routing keys |
136+
| Observability | Include CorrelationId in every message |
137+
| Reliability | Use Outbox pattern for event publishing |
138+
| Maintainability | Group all acknowledgments under `common.ack.events` exchange |

0 commit comments

Comments
 (0)