Order service (and a shared tenant data helper)
Add the second business microservice, orders. It works exactly like products. To avoid copying the tenant plumbing, the sharding and scoped-credential code moves into the shared layer, so both services use it.
| Method | Path | Body |
|---|---|---|
| GET | /orders |
|
| POST | /orders |
{"orderName": "...", "orderProducts": [{"productId": "...", "price": 10.5, "quantity": 2}]} |
| GET / PUT / DELETE | /orders/{shardId:orderId} |
PUT takes the same body as POST |
Table: partition key shardId (<tenantId>-<1..9>), sort key orderId. The line items are stored as a list of maps in orderProducts.
Refactor: tenant_data.py in the layer
Section titled “Refactor: tenant_data.py in the layer”Step 05 put the “tenant data” logic inside the product service. Now that a second service needs it, it lives in server/src/layers/tenant_data.py:
| Function | Does |
|---|---|
get_table(event, table_name) |
Pooled: boto3 table built from the authorizer’s scoped credentials. Silo: the function’s own role. |
new_shard_id(tenant_id) |
tenant-a-<1..9> |
split_key(event, key) |
Splits shardId:itemId and rejects shards of other tenants (PermissionError → 403) |
query_all_shards(table, tenant_id) |
Queries the tenant’s 9 shards in parallel. Errors are re-raised instead of being lost in threads. |
The service code now only knows its own entity. This is the “hide tenant details in a layer” pattern again: a developer adding a third service gets isolation for free by calling these helpers.
Also in this step:
utils.encode_to_json_objectnow usesjson.dumpswith a smalldefaulthandler (Decimal → int/float, objects →__dict__, datetimes → ISO). The jsonpickle and simplejson dependencies are gone, along with their deprecation warnings.- Test setup (moto table + importing a service) moved into a
load_servicefixture in server/tests/conftest.py.
- server/src/OrderService/:
order_service.py(handlers),order_service_dal.py,order_models.py - server/src/layers/tenant_data.py: shared helpers (new)
- server/src/ProductService/product_service_dal.py: now uses
tenant_data - server/cdk/lib/tenant-template/services.ts: second
Microservice, outputOrderTableName - server/tests/test_order_service.py
Deploy
Section titled “Deploy”./scripts/deploy.sh you@example.comVerify
Section titled “Verify”A=$(./scripts/tenant-token.sh alice 'Passw0rd!Saas'); B=$(./scripts/tenant-token.sh bob 'Passw0rd!Saas')curl -s -X POST -H "Authorization: Bearer $A" ${API}orders \ -d '{"orderName":"Alice order","orderProducts":[{"productId":"p1","price":10.5,"quantity":2}]}'curl -s -H "Authorization: Bearer $A" ${API}ordersResults from the test deployment:
| Call | Result |
|---|---|
| alice creates an order | {"key":"tenant-a-4:aec7...","orderName":"Alice order","orderProducts":[{"price":10.5,"productId":"p1","quantity":2}],...} |
| alice PUT her own order | 200, quantity changed to 3 |
| alice lists | only Alice order v2 |
| bob lists | only Bob order |
| alice GET / DELETE bob’s order | 403 each |
alice’s scoped credentials GetItem on bob’s order |
AccessDeniedException |
| products after the refactor | still ['Alice Gadget', 'Alice Widget v2'] |
Unit tests: 34 passed.
Step 07 adds tenant user management: tenant admins can create and manage users of their own tenant only.