ATTENTION! this is a DRAFT document, which might have inconsistencies, errors, and omissions. Take that into consideration.
Fabio Eduardo do Amaral Correa (@feamcor)
Let's Get Rusty - Live Rust Accelerator – Capstone Project
Telecom Business Support Systems (BSS) are traditionally implemented as large, tightly coupled platforms, often bound to vendor-specific stacks, heavyweight enterprise frameworks (usually in Java), and rigid deployment models.
While modern telecom initiatives emphasize cloud-native execution, automation, and scalability, many such efforts conflate execution architecture with business semantics, resulting in systems that scale operationally but lose conceptual clarity.
This project proposes the development of a canonical BSS core platform written in Rust, grounded in telecom business semantics, but executed using modern, cloud-native, event-driven architectural patterns.
The system explicitly separates:
- Business meaning (products, customers, orders, services)
- Execution mechanics (async IO, events, orchestration)
- Infrastructure concerns (databases, messaging, deployment)
The result is a platform that is:
- Cloud-native by design, but cloud-agnostic in execution
- Developed and validated locally
- Deployable to cloud infrastructure without architectural changes
From the learning and practicing perspective, this project is an example of a real-life industry-related business-oriented system, written in idiomatic Rust, leveraging the richness and expressivenes of Rust's data types, features, performance, and ecosystem.
The project uses the TMForum's Information Framework (SID) as a semantic reference, specifically:
- SID R23 as the baseline version
- Used as a conceptual and vocabulary reference, not as a schema to be implemented verbatim
The system deliberately implements a minimal, curated subset of SID concepts aligned with Domain-Driven Design (DDD).
- Hexagonal Architecture (ports & adapters)
- Command / Query Responsibility Segregation (CQRS)
- Event-driven Architecture
- Modular Monolith
This avoids both:
- A traditional monolith with shared mutable state
- A microservice architecture with excessive operational overhead
You might be asking ... hey! you mentioned cloud-native but there are no microservices, so it is not cloud-native, right? ... public cloud providers might say so, but they are biased (selling services), and microservices, like any over-hyped silver-bullet save-the-world technology, is not for everyone or use case. So, allow me this poetic license :)
- Language: Rust
- Async Runtime: Tokio
- Web Framework: Axum
- Observability: Tracing
- Operating System: Linux
Rust is used intentionally to:
- Enforce ownership and aggregate boundaries
- Encode invariants through rich data types
- Achieve high performance and predictable async execution
The initial version focuses on four core domains:
- Party / Customer
- Product
- Order
- Service
Resource management, billing, charging, SLA, and assurance are explicitly out of scope for the initial capstone and considered future extensions.
Entities
- Party
- PartyRole
- Customer
- CustomerAccount
Key Rules
- Party represents a legal or physical actor
- Customer is a role, not a standalone entity
- CustomerAccount is the commercial and ownership boundary
erDiagram
Party ||--o{ PartyRole: plays
PartyRole ||--o{ Customer: becomes
Customer ||--o{ CustomerAccount: has
Coverage
- Customer Relationship Management (CRM)
- Customer onboarding
- Account lifecycle
- Ownership of products
| Field | Type | Notes |
|---|---|---|
| party_id | PartyId(u64) | Identifier |
| party_type | PartyType {Individual, Organization} | |
| status | PartyStatus {Active, Inactive} | Lifecycle |
| valid_for | TimePeriod | |
| created_at | DateTime | Audit |
| created_by | UserId | Audit |
| Field | Type | Notes |
|---|---|---|
| party_role_id | PartyRoleId(u64) | Identifier |
| party_id | PartyId(u64) | Owner |
| role_type | PartyRoleType {Customer, Supplier, Partner} | |
| valid_for | TimePeriod | |
| created_at | DateTime | Audit |
| created_by | UserId | Audit |
| Field | Type | Notes |
|---|---|---|
| customer_id | CustomerId(u64) | Identifier |
| party_role_id | PartyRoleId(u64) | Customer role |
| status | CustomerStatus {Active, Suspended, Terminated} | Lifecycle |
| valid_for | TimePeriod | |
| created_at | DateTime | Audit |
| created_by | UserId | Audit |
| Field | Type | Notes |
|---|---|---|
| account_id | AccountId(String) | Billing anchor |
| customer_id | CustomerId(u64) | Owner |
| account_type | CustomerAccountType {Retail, Wholesale} | |
| status | CustomerAccountStatus {Active, Suspended, Closed} | Lifecycle |
| valid_for | TimePeriod | |
| created_at | DateTime | Audit |
| created_by | UserId | Audit |
Entities
- ProductSpecification
- ProductOffering
- ProductRelationship
- Product
Key Rules
- Specifications are immutable definitions
- Offerings represent market-facing commercial constructs
- Product is the purchased, lifecycle-managed instance
erDiagram
ProductSpecification ||--o{ ProductOffering: defines
ProductOffering ||--o{ Product: instantiates
Coverage
- Product Lifecycle Management
- Product acquisition
- Change product
- Suspend / resume product
- Terminate product
| Field | Type | Notes |
|---|---|---|
| product_spec_id | ProductSpecId(u64) | Identifier |
| name | ProductName(String) | |
| description | String | |
| product_type | ProductType {CFS, Bundle, AddOn} | |
| valid_for | TimePeriod | |
| version | ProductVersion(u32) | Catalog evolution |
| Field | Type | Notes |
|---|---|---|
| offering_id | OfferingId(u64) | |
| product_spec_id | ProductSpecId(u64) | |
| name | OfferingName(String) | |
| description | String | |
| status | OfferingStatus {Active, Retired} | |
| channels | Vec<SalesChannel(String)> | |
| valid_for | TimePeriod |
| Field | Type | Notes |
|---|---|---|
| product_id | ProductId(u64) | Identifier |
| product_spec_id | ProductSpecId(u64) | |
| customer_account_id | AccountId(String) | Ownership |
| status | ProductStatus {Designed, Ordered, Active, Suspended, Terminated} | Lifecycle |
| start_date | DateTime | |
| end_date | DateTime | |
| characteristics | Map<String, Value> | Flexible attributes |
| Field | Type | Notes |
|---|---|---|
| relationship_id | RelationshipId(u64) | Identifier |
| source_product_id | ProductId(u64) | |
| target_product_id | ProductId(u64) | |
| relationship_type | ProductRelationshipType {Bundle, Dependency, AddOn} |
Entities
- ServiceSpecification
- Service
Rules
- Services represent technical delivery
- Services are lifecycle-managed independently of products
- A product may map to one or more services
classDiagram
ServiceSpecification <|-- Service
Entities
- ProductOrder
- ProductOrderItem
- ServiceOrder
Rules
- Orders represent intent
- Orders do not directly mutate domain state
- Orders trigger commands and orchestration
classDiagram
ProductOffering <|-- ProductOrder
ProductOrder <|-- ProductOrderItem
The system is designed to integrate with TMForum OpenAPIs as external contracts, not as internal models.
Relevant APIs include (non-exhaustive):
- TMF620 – Product Catalog
- TMF622 – Product Ordering
- TMF632 – Party Management
- TMF638 – Service Inventory
TMForum OpenAPIs API schemas are:
- Consumed via Data Transfer Objects (DTOs)
- Mapped through an anti-corruption layer
- Never persisted directly
C4Context
title "BSS Core Platform - System Context"
Person(client, "Client", "CLI tools and REST API consumers")
System(bss, "Bare BSS Platform", "Core BSS system exposing APIs and emitting domain events")
System_Ext(rdbms, "Relational Database", "Persistent storage for BSS domain data")
System_Ext(eventBus, "Event Bus", "Asynchronous event distribution")
Rel(client, bss, "Uses", "CLI / REST")
Rel(bss, rdbms, "Persists data to", "SQL")
Rel(bss, eventBus, "Publishes events to", "Async events")
C4Container
title BSS Core Platform – Container Diagram
Person(client, "Client", "CLI user or REST API consumer")
System_Boundary(bss, "Bare BSS Platform (Rust)") {
Container(restApi, "REST API", "Axum", "HTTP API for external clients")
Container(cli, "CLI Interface", "Rust CLI", "Command-line interaction")
Container(app, "Application Layer", "Rust", "Commands, queries, orchestration, use cases")
Container(domain, "Domain Layer", "Rust", "Aggregates, domain rules, domain events")
Container(persistenceAdapters, "Persistence Adapters", "Rust", "Database adapters (e.g. SQLite/Postgres)")
Container(eventingAdapters, "Eventing Adapters", "Rust", "Event publication adapters (in-memory, Kafka, Iggy)")
}
ContainerDb(rdbms, "RDBMS", "SQL Database", "Persistent storage")
Container(eventBus, "Event Bus", "Pub/Sub System", "Asynchronous event distribution")
Rel(client, restApi, "Uses", "HTTP/JSON")
Rel(client, cli, "Uses", "CLI commands")
Rel(restApi, app, "Invokes")
Rel(cli, app, "Invokes")
Rel(app, domain, "Executes domain logic")
Rel(domain, persistenceAdapters, "Persists state via")
Rel(domain, eventingAdapters, "Emits domain events via")
Rel(persistenceAdapters, rdbms, "Reads/Writes", "SQL")
Rel(eventingAdapters, eventBus, "Publishes events to")
bare-bss/
├─ crates/
│ ├─ domain/
│ │ ├─ party/
│ │ ├─ product/
│ │ ├─ service/
│ │ ├─ order/
│ │ └─ common/
│ │
│ ├─ application/
│ │ ├─ commands/
│ │ ├─ queries/
│ │ ├─ orchestration/
│ │ └─ ports/
│ │
│ ├─ infrastructure/
│ │ ├─ persistence/
│ │ │ ├─ sqlite/
│ │ │ └─ postgres/
│ │ ├─ eventing/
│ │ │ ├─ in_memory/
│ │ │ └─ iggy/
│ │ └─ observability/
│ │
│ ├─ api/
│ │ └─ rest/
│ │
│ └─ cli/
│
└─ docker/
#[async_trait]
pub trait ProductRepository {
async fn load(&self, id: ProductId) -> Result<Product>;
async fn save(&self, product: Product) -> Result<()>;
}
...#[async_trait]
pub trait EventBus {
async fn publish(&self, event: DomainEvent);
}pub trait Clock {
fn now(&self) -> DateTime<Utc>;
}- Development: in-memory pub/sub, Tokio channels, deterministic ordering
- Testing/Production: Apache Iggy
- All behind the same EventBus port.
- Development: SQLite
- Testing/Production: PostgreSQL
- SQLx with compile-time query checking.
- One schema per bounded context.
- Docker / Docker Compose
- SQLite
- Optional Postgres (integration tests)
The local environment is treated as one valid deployment target, not a special case.
- Tracing for structured logs.
- Spans across:
- Commands
- Event handlers
- Orchestration flows
- Designed for compatibility with OpenTelemetry exporters.
- No GUI
- No full billing or charging
- No full resource inventory
- No microservice decomposition
- No vendor-specific cloud services
By the end of the capstone, the project will demonstrate:
- A SID-aligned canonical business model
- Clean Rust-based domain modeling
- Event-driven orchestration
- Cloud-native, cloud-agnostic execution
- Production-grade architectural discipline
- Enterprise system developed in Rust
- Billing & charging
- Usage mediation
- SLA & assurance
- Resource modeling
- Service decomposition into deployable units
- TMForum SID R23
- TMForum OpenAPIs (TMF620, TMF622, TMF632, TMF638)
- Domain-Driven Design (Evans)
- Hexagonal Architecture (Ports & Adapters)
- Rust documentation
- Tokio ecosystem documentation
- SQLite documentation
- Postgres documentation
- Apache Iggy documentation