Skip to content

Service split — current, planned, migrating

One system three times: Billing inside one application on a shared database, Billing as its own service with its own database, and the state in between with @migration_target / @planned.

View the source on GitHub

System view
System view
Org view
Org view
// service-split/index.krs: the CURRENT architecture.
// The subject of the first-run guide (#3563). Compare it with planned.krs
// (the target) and migrating.krs (the state in between).
//
// Ordering and Billing are separate domains, but both live in one
// Commerce Application and share one Commerce Database. The Billing Team
// owns the Billing domain without owning any service.
system Commerce {
label "Commerce Platform"
user Customer [human] {
label "Customer"
description "Places orders and pays for them"
}
user FinanceStaff [human] {
label "Finance staff"
description "Reviews invoices and refunds"
}
service CommerceApp {
label "Commerce Application"
description "One deployable that holds ordering and billing"
domain Ordering {
label "Ordering"
usecase PlaceOrder {
label "Place an order"
resource CommerceDB.Orders { operations create }
}
usecase TrackOrder {
label "Track an order"
resource CommerceDB.Orders { operations read }
}
Ordering -> Billing "Request an invoice"
}
domain Billing {
label "Billing"
usecase IssueInvoice {
label "Issue an invoice"
resource CommerceDB.Invoices { operations create }
resource CommerceDB.Orders { operations read }
}
usecase CapturePayment {
label "Capture a payment"
resource CommerceDB.Payments { operations create }
}
}
}
service InventoryWorker {
label "Inventory Worker"
description "Reserves and releases stock in the background"
domain Inventory {
label "Inventory"
usecase ReserveStock {
label "Reserve stock"
resource CommerceDB.Stock { operations update }
}
}
}
service PaymentGateway [external] {
label "Payment Gateway"
description "Card payments, run by a provider"
}
database CommerceDB {
label "Commerce Database"
description "Shared by every part of the platform"
table Orders { label "Orders" }
table Invoices { label "Invoices" }
table Payments { label "Payments" }
table Stock { label "Stock" }
}
Customer -> CommerceApp "Order and pay"
FinanceStaff -> CommerceApp "Review invoices"
CommerceApp -> InventoryWorker "Reserve stock"
CommerceApp -> PaymentGateway "Capture payments"
}
organization Shop {
label "Shop Engineering"
team commerce {
label "Commerce Team"
description "Builds the storefront and the ordering flow"
owns CommerceApp
owns Ordering
}
team billing {
label "Billing Team"
description "Owns the invoicing rules, but ships them inside the Commerce Application"
owns Billing
}
team platform {
label "Platform Team"
description "Runs the shared database and the background workers"
owns InventoryWorker
owns Inventory
}
}
System view
System view
Org view
Org view
// service-split/planned.krs: the TARGET architecture.
// Billing runs as its own service with its own database, and the team
// that owns the billing rules owns the service that runs them.
// Compare it with index.krs (the current architecture).
system Commerce {
label "Commerce Platform"
user Customer [human] {
label "Customer"
description "Places orders and pays for them"
}
user FinanceStaff [human] {
label "Finance staff"
description "Reviews invoices and refunds"
}
service CommerceApp {
label "Commerce Application"
description "Storefront and ordering"
domain Ordering {
label "Ordering"
usecase PlaceOrder {
label "Place an order"
resource CommerceDB.Orders { operations create }
}
usecase TrackOrder {
label "Track an order"
resource CommerceDB.Orders { operations read }
}
}
}
service BillingService {
label "Billing Service"
description "Invoices and payments, own release cycle"
domain Billing {
label "Billing"
usecase IssueInvoice {
label "Issue an invoice"
resource BillingDB.Invoices { operations create }
}
usecase CapturePayment {
label "Capture a payment"
resource BillingDB.Payments { operations create }
}
}
}
service InventoryWorker {
label "Inventory Worker"
description "Reserves and releases stock in the background"
domain Inventory {
label "Inventory"
usecase ReserveStock {
label "Reserve stock"
resource CommerceDB.Stock { operations update }
}
}
}
service PaymentGateway [external] {
label "Payment Gateway"
description "Card payments, run by a provider"
}
database CommerceDB {
label "Commerce Database"
description "Orders and stock"
table Orders { label "Orders" }
table Stock { label "Stock" }
}
database BillingDB {
label "Billing Database"
description "Owned by billing alone"
table Invoices { label "Invoices" }
table Payments { label "Payments" }
}
Customer -> CommerceApp "Order"
Customer -> BillingService "Pay"
FinanceStaff -> BillingService "Review invoices"
CommerceApp -> BillingService "Request an invoice"
CommerceApp -> InventoryWorker "Reserve stock"
BillingService -> PaymentGateway "Capture payments"
}
organization Shop {
label "Shop Engineering"
team commerce {
label "Commerce Team"
description "Builds the storefront and the ordering flow"
owns CommerceApp
owns Ordering
}
team billing {
label "Billing Team"
description "Owns the invoicing rules and the service that runs them"
owns BillingService
owns Billing
}
team platform {
label "Platform Team"
description "Runs the shared database and the background workers"
owns InventoryWorker
owns Inventory
}
}
System view
System view
Org view
Org view
// service-split/migrating.krs: the state IN BETWEEN.
// Billing Service is running, but it still reads and writes the shared
// Commerce Database: its own Billing Database is planned, not built.
// Moving the data is the step that decides when the split is done.
system Commerce {
label "Commerce Platform"
user Customer [human] {
label "Customer"
description "Places orders and pays for them"
}
user FinanceStaff [human] {
label "Finance staff"
description "Reviews invoices and refunds"
}
service CommerceApp {
label "Commerce Application"
description "Storefront and ordering"
domain Ordering {
label "Ordering"
usecase PlaceOrder {
label "Place an order"
resource CommerceDB.Orders { operations create }
}
usecase TrackOrder {
label "Track an order"
resource CommerceDB.Orders { operations read }
}
}
}
service BillingService @migration_target(from: CommerceApp) {
label "Billing Service"
description "Split out, still on the shared database"
domain Billing {
label "Billing"
usecase IssueInvoice {
label "Issue an invoice"
resource CommerceDB.Invoices { operations create }
resource CommerceDB.Orders { operations read }
}
usecase CapturePayment {
label "Capture a payment"
resource CommerceDB.Payments { operations create }
}
}
}
service InventoryWorker {
label "Inventory Worker"
description "Reserves and releases stock in the background"
domain Inventory {
label "Inventory"
usecase ReserveStock {
label "Reserve stock"
resource CommerceDB.Stock { operations update }
}
}
}
service PaymentGateway [external] {
label "Payment Gateway"
description "Card payments, run by a provider"
}
database CommerceDB {
label "Commerce Database"
description "Billing tables have not moved yet"
table Orders { label "Orders" }
table Invoices @deprecated { label "Invoices" }
table Payments @deprecated { label "Payments" }
table Stock { label "Stock" }
}
database BillingDB @planned {
label "Billing Database"
description "Where the billing tables will move"
table Invoices { label "Invoices" }
table Payments { label "Payments" }
}
Customer -> CommerceApp "Order"
Customer -> BillingService "Pay"
FinanceStaff -> BillingService "Review invoices"
CommerceApp -> BillingService "Request an invoice"
CommerceApp -> InventoryWorker "Reserve stock"
BillingService -> PaymentGateway "Capture payments"
}
organization Shop {
label "Shop Engineering"
team commerce {
label "Commerce Team"
description "Builds the storefront and the ordering flow"
owns CommerceApp
owns Ordering
}
team billing {
label "Billing Team"
description "Runs Billing Service; depends on the Platform Team for its data until the move"
owns BillingService
owns Billing
}
team platform {
label "Platform Team"
description "Runs the shared database and the background workers"
owns InventoryWorker
owns Inventory
}
}

© 2026 Hiroki Kondo · Licensed underApache-2.0

Built with Cloudflare