A gRPC-based microservice for managing Slowly Changing Dimensions (SCD) using versioned data models.
This service supports Job, Timelog, and PaymentLineItem entities with version history, immutability, and centralized update/query logic.
In distributed systems, it's critical to preserve historical versions of entities while enabling efficient access to the most recent one. This pattern, known as Slowly Changing Dimensions (SCD), enables:
- Auditability and rollback
- Temporal analytics
- Immutable data history
- Safe concurrent updates
This service implements SCD Type 2 using versioned rows, allowing consumers to retrieve the latest versions and make updates without losing historical context.
-
Avoid code duplication:
-
Easier maintenance and feature updates:
-
Consistent SCD handling:
-
Performance optimizations:
-
Language agnostic access:
- Language: Go
- Framework: gRPC + Protobuf
- ORM: GORM (SQLite)
- Client Support: Go, Python (via gRPC stubs)
Each entity has a logical ID (shared across versions) and a unique UID (per version).
The latest version is determined by the highest version number per UID.
Updates are append-only operations that preserve history.
Clients can query the latest version via dedicated APIs like GetLatestJobs.
All versioning and querying logic is centralized in this Go service, avoiding duplication across consumers (e.g., Django, other Go microservices).
A shared helper builds GORM queries to fetch the latest version of any model using subqueries and joins, e.g.:
func GetLatestVersionQuery(db *gorm.DB, model interface{}, idColumn string) *gorm.DBUpdate operations read the latest version, increment the version, and insert a new row—ensuring no version conflicts or data loss.
git clone https://github.com/your-org/scd-service.git
cd scd-servicego run main.gogo test ./...protoc --go_out=. --go-grpc_out=. proto/scd.protopython3 -m grpc_tools.protoc -I=proto --python_out=client/python --grpc_python_out=client/python proto/scd.protoUpdateJob("uid1", map[string]string{
"status": "in-progress",
"title": "Backend Developer",
"rate": "25.5",
})UpdateTimelog("tl_uid_2", map[string]string{
"duration": "1800",
"type": "adjusted",
})GetLatestJobs("in-progress")GetLatestTimelogs("adjusted")We considered DB views that materialize only the latest versions using MAX(version) and group-by-UID logic. While this simplifies client access, it:
-
Adds DB schema complexity
-
Makes version-based filters harder to express
-
Reduces flexibility in applying business rules before returning results
-
We chose the service-based approach to retain flexibility, testability,and centralized control.
-
Add caching layer for frequently requested latest entities
-
Add pagination and filtering to list APIs
-
Support soft deletion and archival
-
Add metrics and monitoring
