go-composable-business-types

module
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT

README

go-composable-business-types

Strongly typed, composable business types for Go — featuring DataPoint[T], a self-contained data unit with complete audit trail built-in.

What is this?

A Go library of type-safe base values designed for business applications. It combines:

  • Branded/phantom types (ID[B, V]) — prevent mixing UserID with OrderID at compile time (via go-branded-id)
  • Audit trail primitivesActorChain[T], Context, Reference[T], Cause[T] for traceability
  • Bitemporal tracking — separate valid time from recorded time
  • Domain primitivesBoundedString, Money, Email, Percentage, Cents, Timestamp, Duration
  • Self-contained data unitsDataPoint[T] wraps any payload with full metadata (who, when, why, what caused it, references, tags)

Selective imports let you use only what you need.

Installation

go get github.com/larsartmann/go-composable-business-types

# For branded IDs (used by actor, datapoint, etc.)
go get github.com/larsartmann/go-branded-id

Types

Type Package Purpose
NanoID nanoid/ URL-safe, cryptographically random ID (default 21 chars)
ActorChain[T] actor/ Ordered chain of actors (User → Service → Service) for audit trails
DataPoint[T] datapoint/ Self-contained data unit with complete audit trail
Bitemporal temporal/ Bitemporal tracking (validFrom, validUntil, recorded)
Context datapoint/ Execution context (environment, session, request, source)
Reference[T] datapoint/ Type-safe reference to another entity with relationship metadata
Cause[T] datapoint/ Causal chain tracking for building audit/lineage graphs
BoundedString bounded/ String with validated length constraints
Money money/ ISO 4217 currency via github.com/bojanz/currency
Locale locale/ BCP 47 language tag for internationalization
Email types/ Email address string
URL types/ URL string
Percentage types/ 0-100 value with float conversion (clamps overflow to 100)
Cents types/ Monetary amount in smallest unit (no float errors)
Timestamp types/ Domain-wrapped time.Time
Duration types/ Domain-wrapped time.Duration
Importance importance/ Priority classification (0-100) with named levels
Tag tag/ Validated string label with alphanumeric+hyphen constraint
Address address/ Validated postal address (line1, city, postalCode, countryCode)
Contact contact/ Contact info (name, email, phone, website, address)
External Types (separate module)
Type Module Purpose
ID[B, V] github.com/larsartmann/go-branded-id Branded, type-safe identifier — prevents mixing IDs

Enums (generated)

Enum Values
ActorKind User, Bot, System, Service
Priority Low, Medium, High, Critical
Status Draft, Active, Paused, Archived, Deleted
Trigger Manual, Scheduled, Webhook, Import, Migration, System, Correction
CauseKind Direct, Command, Event

Usage

// Selective imports - import only what you need
import (
    id "github.com/larsartmann/go-branded-id"
    "github.com/larsartmann/go-composable-business-types/actor"
    "github.com/larsartmann/go-composable-business-types/bounded"
    "github.com/larsartmann/go-composable-business-types/money"
    "github.com/larsartmann/go-composable-business-types/types"
)

// Branded IDs - can't mix UserId with OrderId at compile time
type UserBrand struct{}
type OrderBrand struct{}
type UserID = id.ID[UserBrand, string]
type OrderID = id.ID[OrderBrand, int64]

userId := id.NewID[UserBrand, string]("user-123")
orderId := id.NewID[OrderBrand, int64](42)

// Unbranded ID (when you don't need type separation)
type SessionID = id.ID[struct{}, string]
sessionId := id.NewID[struct{}, string]("sess-abc")

// Actor chain for audit trails and authorization
chain := actor.NewActorChain(actor.UserActor(id.NewID[struct{}, string]("user-1"), "Alice")).
    Append(actor.ServiceActor(id.NewID[struct{}, string]("api-gateway"), "API Gateway")).
    Append(actor.ServiceActor(id.NewID[struct{}, string]("order-svc"), "Order Service"))

origin := chain.Origin()   // User Alice
current := chain.Current() // Order Service

// BoundedString - validated string lengths
name, err := bounded.NewBoundedString(1, 100, "John Doe")
if err != nil {
    // handle validation error
}

// Factory for domain-specific bounded strings
var NewProductName = bounded.BoundedStringOf(1, 200)
product, err := NewProductName("Widget")

// NonEmpty convenience (min=1)
title, err := bounded.NonEmptyString(50, "  trimmed input  ")

// Trimmed input
clean, err := bounded.TrimmedBoundedString(1, 50, "  hello  ") // "hello"

// Money without float errors (via bojanz/currency)
price := types.NewCents(1099) // $10.99
fmt.Println(price.Float64()) // 10.99

// ISO 4217 Money with full currency support
usd, err := money.NewMoney("99.99", "USD")
eur, err := money.NewMoneyFromCents(1099, "EUR") // €10.99

// Format for locale
formatted := money.FormatMoney(usd, "de_DE") // "99,99 €"

// Currency utilities
money.IsValidCurrency("USD")     // true
digits, _ := money.CurrencyDigits("JPY") // 0 (no decimal places)
codes := money.AllCurrencyCodes()         // all ISO 4217 codes

// Percentage (clamped to 0-100)
tax := types.NewPercentage(8)  // 8%
fmt.Println(tax.Float64())   // 0.08

DataPoint[T] - Complete Audit Trail

DataPoint[T] is a self-contained unit of data with complete audit trail. Inspired by event sourcing, it preserves ALL relationships and metadata at the application layer, enabling full traceability without external systems.

Core Features
  • NanoID: Unique, URL-safe identifier (21 chars by default)
  • Bitemporal tracking: validFrom, validUntil, recorded timestamps
  • Actor tracking: Who caused this data point (User, Bot, Service, System)
  • Trigger: What caused this data point (Manual, Scheduled, Webhook, etc.)
  • Context: Execution environment, session, request, source
  • References: Type-safe references to related entities
  • Causes: Causal chain for building audit/lineage graphs
  • Tags: Additional metadata key-value pairs
  • Version: Optimistic concurrency support
Basic Usage
import (
    id "github.com/larsartmann/go-branded-id"
    "github.com/larsartmann/go-composable-business-types/actor"
    "github.com/larsartmann/go-composable-business-types/datapoint"
    "github.com/larsartmann/go-composable-business-types/enums"
)

type OrderState struct {
    OrderId   string
    Status    string
    Total     int
}

// Create a DataPoint
actorEntry := actor.UserActor(id.NewID[struct{}, string]("user-1"), "Alice")
dp := datapoint.NewDataPoint(OrderState{
    OrderId: "order-123",
    Status:  "created",
    Total:   9900,
}, actorEntry).WithReason("customer placed order")

// Access fields
fmt.Println(dp.ID())           // NanoID (unique)
fmt.Println(dp.Payload())      // OrderState
fmt.Println(dp.Actor().Name)   // "Alice"
fmt.Println(dp.Trigger())      // TriggerManual
fmt.Println(dp.Reason())       // "customer placed order"
With Builder Methods
import (
    "github.com/larsartmann/go-composable-business-types/datapoint"
    "github.com/larsartmann/go-composable-business-types/enums"
    "github.com/larsartmann/go-composable-business-types/nanoid"
)

dp := datapoint.NewDataPoint(payload, actorEntry).
    WithTrigger(enums.TriggerWebhook).
    WithReason("webhook received from payment provider").
    WithContext(datapoint.NewContext().
        WithEnvironment("production").
        WithSource("payment-service").
        WithSession("sess-abc")).
    WithVersion(3).
    WithTag("correlation_id", "corr-123").
    WithReference(datapoint.NewReference("order-456", "parent")).
    WithCause(datapoint.NewCauseCommand[string](nanoid.New(), "approved"))
Bitemporal Tracking
import (
    "time"

    "github.com/larsartmann/go-composable-business-types/datapoint"
    "github.com/larsartmann/go-composable-business-types/temporal"
    "github.com/larsartmann/go-composable-business-types/types"
)

// Create with explicit time range
from := types.NewTimestamp(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC))
until := types.NewTimestamp(time.Date(2024, 12, 31, 0, 0, 0, 0, time.UTC))
recorded := types.Now()

temp := temporal.NewBitemporalWithRange(from, until, recorded)
dp := datapoint.NewDataPoint(payload, actorEntry).WithTemporal(temp)

// Check if valid at a point in time
if dp.Temporal().IsValidAt(someTime) {
    // DataPoint was valid at that time
}
References and Causal Chain
import (
    "github.com/larsartmann/go-composable-business-types/datapoint"
    "github.com/larsartmann/go-composable-business-types/nanoid"
)

// Reference to another entity
ref := datapoint.NewReference("doc-123", "source").
    WithVersion(5).
    WithTag("department", "legal")

// Cause tracking (event-triggered)
causeID := nanoid.New()
trace := []nanoid.NanoID{intermediateId}
cause := datapoint.NewCauseEvent[string](causeID, "created", trace...)

dp := datapoint.NewDataPoint(payload, actorEntry).
    WithReference(ref).
    WithCause(cause)
JSON Serialization
import "encoding/json"

// Full JSON support with round-trip
data, _ := json.Marshal(dp)
var parsed datapoint.DataPoint[OrderState]
json.Unmarshal(data, &parsed)

// Fields are preserved: id, payload, actor, temporal, trigger,
// reason, context, version, tags, references, causes

Additional Packages

Package Purpose
validate/ Validator interface for self-validating types
pkg/errors/ Centralized sentinel and structured error definitions
scanutil/ Helpers for implementing sql.Scanner / driver.Valuer
version/ Build version info from runtime/debug.ReadBuildInfo
tag/ Validated Tag type with length and character constraints
importance/ Importance classification (0-100) with named levels
locale/ BCP 47 Locale wrapping golang.org/x/text/language
projectcore/ Composite project metadata type (name, path, languages, tags)

See the examples/ directory for complete working programs.

Generate

Enums are generated with go-enum:

go generate ./...

Dependencies

Package Purpose
github.com/larsartmann/go-branded-id Branded/phantom-type identifiers
github.com/bojanz/currency ISO 4217 currency handling
github.com/sixafter/nanoid FIPS-140 compatible NanoID generation
golang.org/x/text BCP 47 locale/language support
github.com/abice/go-enum Enum code generation (dev only)

Documentation

Document Description
README.md This file — usage and examples
POLICY.md Library policies: versioning, breaking changes, contributions
SUPPORT.md Getting help, reporting issues, FAQ
SECURITY.md Security policy and vulnerability reporting
CHANGELOG.md Version history and release notes
FEATURES.md Honest feature inventory by status
TODO_LIST.md Short-term actionable work items
PARTS.md Component analysis and architecture

Contributing

See POLICY.md for:

  • Contribution guidelines
  • Breaking changes policy
  • Commit message conventions
  • Code review process

Support

License

MIT

Directories

Path Synopsis
Package actor provides types for tracking audit trails and actor chains.
Package actor provides types for tracking audit trails and actor chains.
Package address provides validated postal address types.
Package address provides validated postal address types.
Package bounded provides length-validated string types.
Package bounded provides length-validated string types.
Package contact provides validated contact types.
Package contact provides validated contact types.
datapoint module
Package enums provides generated enumerations for common domain concepts.
Package enums provides generated enumerations for common domain concepts.
Package importance provides a 0-100 priority classification with named levels.
Package importance provides a 0-100 priority classification with named levels.
locale module
money module
nanoid module
pkg
errors
Package errors provides centralized domain-specific errors for the library.
Package errors provides centralized domain-specific errors for the library.
Package projectcore provides a composite type for project metadata.
Package projectcore provides a composite type for project metadata.
Package scanutil provides reusable utilities for sql.Scanner and driver.Valuer implementations.
Package scanutil provides reusable utilities for sql.Scanner and driver.Valuer implementations.
Package tag provides validated string labels with length and character constraints.
Package tag provides validated string labels with length and character constraints.
Package temporal provides bitemporal time tracking.
Package temporal provides bitemporal time tracking.
Package testutil provides generic test helpers for parse/string/zero-value testing.
Package testutil provides generic test helpers for parse/string/zero-value testing.
Package types provides common domain types for business applications.
Package types provides common domain types for business applications.
Package validate provides interfaces for common validation patterns.
Package validate provides interfaces for common validation patterns.
Package version provides build-time version information extracted from runtime/debug.
Package version provides build-time version information extracted from runtime/debug.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL