Guides
Adopting semantic types
Introducing Semantic Types
Evolution, Not Revolution
Successfully adopting semantic types is an incremental process. Start small, demonstrate value, then expand gradually.
Getting Started
Begin by identifying a focused use case:
- Find two systems that exchange similar data
- Identify overlapping concepts between their models
- Create shared semantic types for these concepts
Example:
// System A's original model
model CustomerA {
id: String
email: String
}
// System B's original model
model CustomerB {
customerId: String
emailAddress: String
}
// Create shared semantic types
type CustomerId inherits String
type EmailAddress inherits String
// Refactor models to use semantic types
model CustomerA {
id: CustomerId
email: EmailAddress
}
model CustomerB {
customerId: CustomerId
emailAddress: EmailAddress
}
Project Organization
Separate Types from Models
Types are meant to be shared across systems, while models are system-specific. Your project structure should reflect this separation.
File Organization
Start with a simple separation:
src/
types/ # Shared semantic types (your taxonomy)
customer.taxi
order.taxi
models/ # System-specific models
service-a/
customer-model.taxi
service-definitions.taxi
service-b/
customer.api.taxi # A REST API exposing customer details. Has both model and services defined
As your taxonomy grows, consider moving types to their own project:
projects/
common-taxonomy/ # Shared semantic types
service-a/ # Service A implementation
dependencies:
- common-taxonomy
service-b/ # Service B implementation
dependencies:
- common-taxonomy
File naming conventions
Types
Define semantic types on their own, in a file named around the domain they model. For example:
customer.types.taxi
account.types.taxi
Also consider...
You may also wish to consider adopting separate namespaces, which further help keep concepts seperated.
Services and Models
Define services and models together, in the same file. Name the file after the type of service.
eg:
trades.kafka.taxi
account-events.kafka.taxi
customer.api.taxi
account.database.taxi
Mature Implementation Architecture
The Big Picture
A well-implemented Taxi ecosystem has clear separation between shared semantics and system-specific implementations.
A mature implementation typically includes:
Shared Taxonomy
- Collection of semantic types
- Broadly shared across organization
- Version controlled and carefully governed
- Published as a reusable package
Service Implementations
- Models and service definitions using types from taxonomy
- System-specific structures
- Published to TaxiQL server (like Orbital)
- Each service depends on shared taxonomy
Data Consumers
- Import shared taxonomy only
- Don’t depend on service-specific models
- Query data using TaxiQL
- Receive data mapped to their needs
Example workflow:
// In shared taxonomy
type CustomerId inherits String
type EmailAddress inherits String
// In service implementation
service CustomerService {
operation getCustomer(CustomerId): Customer
}
// Consumer query using TaxiQL
find { Customer } as {
id: CustomerId
contact: EmailAddress
}
Best Practices
Type Development
- Focus on business concepts
- Keep types focused and single-purpose
- Document type meanings clearly
- Version types carefully
Model Development
- Use semantic types for fields
- Keep models service-specific
- Don’t share models between services
Service Integration
- Publish service contracts to TaxiQL server
- Use semantic types in operation signatures
- Let TaxiQL handle data mapping
Measuring Success
Your implementation is successful when:
- Services can evolve independently
- Data integration requires minimal code
- New consumers can easily discover and use data
- Changes to one service don’t cascade to others
- Semantic meaning is preserved across systems