diff --git a/docs/architecture/catalog-contract-boundary.puml b/docs/architecture/catalog-contract-boundary.puml index 570a67c..0981c8a 100644 --- a/docs/architecture/catalog-contract-boundary.puml +++ b/docs/architecture/catalog-contract-boundary.puml @@ -3,16 +3,24 @@ skinparam packageStyle rectangle package "building-block-catalog" { package "BuildingBlock.Catalog.Contracts" { + class Conventions class Products class Tags + interface Adapters + class Grpc interface Abstractions } } +package "blueprint-platform" { + interface IBlueprintPackageContract +} + package "furniture-dal" as FurnitureDal package "furniture-service" as FurnitureService package "furniture-bff" as FurnitureBff +Conventions ..> IBlueprintPackageContract FurnitureDal --> Products FurnitureDal --> Tags FurnitureService --> Products diff --git a/docs/contracts/catalog-contract-catalog.md b/docs/contracts/catalog-contract-catalog.md index c62c4d3..4040e0d 100644 --- a/docs/contracts/catalog-contract-catalog.md +++ b/docs/contracts/catalog-contract-catalog.md @@ -6,12 +6,16 @@ ## Contract Groups -- `Products`: product contract shapes. -- `Tags`: tag, tag type, and tag override contract shapes. +- `Conventions`: transport-neutral request/response and envelope conventions. +- `Products`: transport-neutral product contract shapes. +- `Tags`: transport-neutral tag, tag type, and tag override contract shapes. - `Abstractions`: marker abstraction for contract ownership. +- `Adapters`: protocol adapter boundaries. +- `Grpc`: gRPC contract shapes for adapter translation. ## Ownership Boundary - This repository owns reusable catalog capability contracts. -- Persistence and transport concerns remain outside this package. +- Contract metadata consumes `Core.Blueprint.Common.Contracts` and does not redefine Blueprint contracts. +- Persistence and transport implementations remain outside this package. - Identity abstractions remain Thalos-owned. diff --git a/docs/contracts/catalog-versioning-policy.md b/docs/contracts/catalog-versioning-policy.md index c4ba152..a859889 100644 --- a/docs/contracts/catalog-versioning-policy.md +++ b/docs/contracts/catalog-versioning-policy.md @@ -6,7 +6,12 @@ - Breaking changes require major version increments. - Deprecated members remain through one deprecation cycle. +## Blueprint Compatibility + +- Package descriptor metadata is implemented via `IBlueprintPackageContract` from `Core.Blueprint.Common.Contracts`. +- Catalog contracts consume Blueprint common contract primitives rather than redefining them. + ## Compatibility Notes - Consumers (`furniture-dal`, `furniture-service`, `furniture-bff`) update explicitly. -- Transport-specific adapters are out of scope for this contracts package. +- Protocol adapters remain edge concerns; catalog contracts remain transport-neutral. diff --git a/src/BuildingBlock.Catalog.Contracts/Adapters/ICatalogGrpcContractAdapter.cs b/src/BuildingBlock.Catalog.Contracts/Adapters/ICatalogGrpcContractAdapter.cs new file mode 100644 index 0000000..bf0e6ee --- /dev/null +++ b/src/BuildingBlock.Catalog.Contracts/Adapters/ICatalogGrpcContractAdapter.cs @@ -0,0 +1,25 @@ +using BuildingBlock.Catalog.Contracts.Grpc; +using BuildingBlock.Catalog.Contracts.Products; +using BuildingBlock.Catalog.Contracts.Responses; + +namespace BuildingBlock.Catalog.Contracts.Adapters; + +/// +/// Defines adapter boundary for catalog gRPC contract translation. +/// +public interface ICatalogGrpcContractAdapter +{ + /// + /// Maps transport-neutral product contract into gRPC shape. + /// + /// Transport-neutral product contract. + /// gRPC product contract shape. + CatalogProductGrpcContract ToGrpcProduct(ProductContract contract); + + /// + /// Maps gRPC product contract into transport-neutral response. + /// + /// gRPC product contract shape. + /// Transport-neutral product response contract. + ProductContractResponse FromGrpcProduct(CatalogProductGrpcContract contract); +} diff --git a/src/BuildingBlock.Catalog.Contracts/BuildingBlock.Catalog.Contracts.csproj b/src/BuildingBlock.Catalog.Contracts/BuildingBlock.Catalog.Contracts.csproj index 6c3a887..04a4bbc 100644 --- a/src/BuildingBlock.Catalog.Contracts/BuildingBlock.Catalog.Contracts.csproj +++ b/src/BuildingBlock.Catalog.Contracts/BuildingBlock.Catalog.Contracts.csproj @@ -4,4 +4,7 @@ enable enable + + + diff --git a/src/BuildingBlock.Catalog.Contracts/Conventions/CatalogContractEnvelope.cs b/src/BuildingBlock.Catalog.Contracts/Conventions/CatalogContractEnvelope.cs new file mode 100644 index 0000000..0a93306 --- /dev/null +++ b/src/BuildingBlock.Catalog.Contracts/Conventions/CatalogContractEnvelope.cs @@ -0,0 +1,8 @@ +namespace BuildingBlock.Catalog.Contracts.Conventions; + +/// +/// Defines transport-neutral envelope metadata for catalog contract messages. +/// +/// Contract schema version. +/// Correlation identifier for cross-service tracing. +public sealed record CatalogContractEnvelope(string ContractVersion, string CorrelationId); diff --git a/src/BuildingBlock.Catalog.Contracts/Conventions/CatalogPackageContract.cs b/src/BuildingBlock.Catalog.Contracts/Conventions/CatalogPackageContract.cs new file mode 100644 index 0000000..125382c --- /dev/null +++ b/src/BuildingBlock.Catalog.Contracts/Conventions/CatalogPackageContract.cs @@ -0,0 +1,15 @@ +using Core.Blueprint.Common.Contracts; + +namespace BuildingBlock.Catalog.Contracts.Conventions; + +/// +/// Defines package descriptor metadata for catalog contracts package. +/// +public sealed class CatalogPackageContract : IBlueprintPackageContract +{ + /// + public BlueprintPackageDescriptor Descriptor { get; } = new( + "BuildingBlock.Catalog.Contracts", + PackageVersionPolicy.Minor, + ["Core.Blueprint.Common"]); +} diff --git a/src/BuildingBlock.Catalog.Contracts/Conventions/ICatalogContractRequest.cs b/src/BuildingBlock.Catalog.Contracts/Conventions/ICatalogContractRequest.cs new file mode 100644 index 0000000..7dbf204 --- /dev/null +++ b/src/BuildingBlock.Catalog.Contracts/Conventions/ICatalogContractRequest.cs @@ -0,0 +1,9 @@ +namespace BuildingBlock.Catalog.Contracts.Conventions; + +/// +/// Defines transport-neutral request contract shape for catalog capability operations. +/// +/// Response contract type. +public interface ICatalogContractRequest +{ +} diff --git a/src/BuildingBlock.Catalog.Contracts/Grpc/CatalogProductGrpcContract.cs b/src/BuildingBlock.Catalog.Contracts/Grpc/CatalogProductGrpcContract.cs new file mode 100644 index 0000000..9f8d0f1 --- /dev/null +++ b/src/BuildingBlock.Catalog.Contracts/Grpc/CatalogProductGrpcContract.cs @@ -0,0 +1,8 @@ +namespace BuildingBlock.Catalog.Contracts.Grpc; + +/// +/// Defines minimal gRPC contract shape for catalog product adapter translation. +/// +/// Product identifier. +/// Product display name. +public sealed record CatalogProductGrpcContract(string ProductId, string DisplayName); diff --git a/src/BuildingBlock.Catalog.Contracts/Products/ProductContract.cs b/src/BuildingBlock.Catalog.Contracts/Products/ProductContract.cs index 51030af..c7be35f 100644 --- a/src/BuildingBlock.Catalog.Contracts/Products/ProductContract.cs +++ b/src/BuildingBlock.Catalog.Contracts/Products/ProductContract.cs @@ -1,8 +1,15 @@ +using BuildingBlock.Catalog.Contracts.Conventions; + namespace BuildingBlock.Catalog.Contracts.Products; /// -/// Catalog product contract. +/// Transport-neutral catalog product contract. /// +/// Contract envelope metadata. /// Product identifier in catalog capability scope. /// Product display name. -public sealed record ProductContract(string ProductId, string DisplayName); +public sealed record ProductContract( + CatalogContractEnvelope Envelope, + string ProductId, + string DisplayName) + : ICatalogContractRequest; diff --git a/src/BuildingBlock.Catalog.Contracts/Responses/ProductContractResponse.cs b/src/BuildingBlock.Catalog.Contracts/Responses/ProductContractResponse.cs new file mode 100644 index 0000000..06a424b --- /dev/null +++ b/src/BuildingBlock.Catalog.Contracts/Responses/ProductContractResponse.cs @@ -0,0 +1,14 @@ +using BuildingBlock.Catalog.Contracts.Conventions; + +namespace BuildingBlock.Catalog.Contracts.Responses; + +/// +/// Transport-neutral catalog product response contract. +/// +/// Contract envelope metadata. +/// Product identifier in catalog capability scope. +/// Product display name. +public sealed record ProductContractResponse( + CatalogContractEnvelope Envelope, + string ProductId, + string DisplayName); diff --git a/src/BuildingBlock.Catalog.Contracts/Tags/TagContract.cs b/src/BuildingBlock.Catalog.Contracts/Tags/TagContract.cs index 5c58050..41596c2 100644 --- a/src/BuildingBlock.Catalog.Contracts/Tags/TagContract.cs +++ b/src/BuildingBlock.Catalog.Contracts/Tags/TagContract.cs @@ -1,9 +1,16 @@ +using BuildingBlock.Catalog.Contracts.Conventions; + namespace BuildingBlock.Catalog.Contracts.Tags; /// -/// Catalog tag contract. +/// Transport-neutral catalog tag contract. /// +/// Contract envelope metadata. /// Tag identifier. /// Tag type identifier. /// Tag value. -public sealed record TagContract(string TagId, string TagTypeId, string Value); +public sealed record TagContract( + CatalogContractEnvelope Envelope, + string TagId, + string TagTypeId, + string Value); diff --git a/src/BuildingBlock.Catalog.Contracts/Tags/TagOverrideContract.cs b/src/BuildingBlock.Catalog.Contracts/Tags/TagOverrideContract.cs index f9dcf94..36cd899 100644 --- a/src/BuildingBlock.Catalog.Contracts/Tags/TagOverrideContract.cs +++ b/src/BuildingBlock.Catalog.Contracts/Tags/TagOverrideContract.cs @@ -1,9 +1,16 @@ +using BuildingBlock.Catalog.Contracts.Conventions; + namespace BuildingBlock.Catalog.Contracts.Tags; /// -/// Catalog tag override contract for consumer-specific overrides. +/// Transport-neutral catalog tag override contract. /// +/// Contract envelope metadata. /// Tag identifier. /// Override target scope. /// Override value. -public sealed record TagOverrideContract(string TagId, string TargetScope, string OverrideValue); +public sealed record TagOverrideContract( + CatalogContractEnvelope Envelope, + string TagId, + string TargetScope, + string OverrideValue); diff --git a/src/BuildingBlock.Catalog.Contracts/Tags/TagTypeContract.cs b/src/BuildingBlock.Catalog.Contracts/Tags/TagTypeContract.cs index b4e1a06..2b29878 100644 --- a/src/BuildingBlock.Catalog.Contracts/Tags/TagTypeContract.cs +++ b/src/BuildingBlock.Catalog.Contracts/Tags/TagTypeContract.cs @@ -1,8 +1,14 @@ +using BuildingBlock.Catalog.Contracts.Conventions; + namespace BuildingBlock.Catalog.Contracts.Tags; /// -/// Catalog tag type contract. +/// Transport-neutral catalog tag type contract. /// +/// Contract envelope metadata. /// Tag type identifier. /// Tag type name. -public sealed record TagTypeContract(string TagTypeId, string Name); +public sealed record TagTypeContract( + CatalogContractEnvelope Envelope, + string TagTypeId, + string Name); diff --git a/tests/BuildingBlock.Catalog.Contracts.UnitTests/ContractShapeTests.cs b/tests/BuildingBlock.Catalog.Contracts.UnitTests/ContractShapeTests.cs index 2013c3b..ddd98b5 100644 --- a/tests/BuildingBlock.Catalog.Contracts.UnitTests/ContractShapeTests.cs +++ b/tests/BuildingBlock.Catalog.Contracts.UnitTests/ContractShapeTests.cs @@ -1,26 +1,57 @@ +using BuildingBlock.Catalog.Contracts.Conventions; using BuildingBlock.Catalog.Contracts.Products; +using BuildingBlock.Catalog.Contracts.Responses; using BuildingBlock.Catalog.Contracts.Tags; +using Core.Blueprint.Common.Contracts; namespace BuildingBlock.Catalog.Contracts.UnitTests; public class ContractShapeTests { [Fact] - public void ProductContract_WhenCreated_StoresRequiredValues() + public void ProductContract_WhenCreated_StoresTransportNeutralValues() { - var contract = new ProductContract("PRD-001", "Chair"); + var envelope = new CatalogContractEnvelope("1.0.0", "corr-123"); + var contract = new ProductContract(envelope, "PRD-001", "Chair"); + Assert.Equal("1.0.0", contract.Envelope.ContractVersion); + Assert.Equal("corr-123", contract.Envelope.CorrelationId); Assert.Equal("PRD-001", contract.ProductId); Assert.Equal("Chair", contract.DisplayName); } [Fact] - public void TagOverrideContract_WhenCreated_StoresRequiredValues() + public void TagOverrideContract_WhenCreated_StoresTransportNeutralValues() { - var contract = new TagOverrideContract("TAG-001", "furniture", "featured"); + var envelope = new CatalogContractEnvelope("1.0.0", "corr-123"); + var contract = new TagOverrideContract(envelope, "TAG-001", "furniture", "featured"); + Assert.Equal("1.0.0", contract.Envelope.ContractVersion); + Assert.Equal("corr-123", contract.Envelope.CorrelationId); Assert.Equal("TAG-001", contract.TagId); Assert.Equal("furniture", contract.TargetScope); Assert.Equal("featured", contract.OverrideValue); } + + [Fact] + public void CatalogPackageContract_WhenCreated_UsesBlueprintDescriptorContract() + { + IBlueprintPackageContract contract = new CatalogPackageContract(); + + Assert.Equal("BuildingBlock.Catalog.Contracts", contract.Descriptor.PackageId); + Assert.Equal(PackageVersionPolicy.Minor, contract.Descriptor.VersionPolicy); + Assert.Contains("Core.Blueprint.Common", contract.Descriptor.DependencyPackageIds); + } + + [Fact] + public void ProductContractResponse_WhenCreated_StoresTransportNeutralValues() + { + var envelope = new CatalogContractEnvelope("1.0.0", "corr-456"); + var response = new ProductContractResponse(envelope, "PRD-001", "Chair"); + + Assert.Equal("1.0.0", response.Envelope.ContractVersion); + Assert.Equal("corr-456", response.Envelope.CorrelationId); + Assert.Equal("PRD-001", response.ProductId); + Assert.Equal("Chair", response.DisplayName); + } }