feat: user service
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"galaxy/user/internal/domain/common"
|
||||
"galaxy/user/internal/domain/entitlement"
|
||||
)
|
||||
|
||||
// EntitlementHistoryStore persists immutable entitlement period records and
|
||||
// later close-state updates.
|
||||
type EntitlementHistoryStore interface {
|
||||
// Create stores one new entitlement period history record. Implementations
|
||||
// must wrap ErrConflict when record.RecordID already exists.
|
||||
Create(ctx context.Context, record entitlement.PeriodRecord) error
|
||||
|
||||
// GetByRecordID returns the entitlement period history record identified by
|
||||
// recordID.
|
||||
GetByRecordID(ctx context.Context, recordID entitlement.EntitlementRecordID) (entitlement.PeriodRecord, error)
|
||||
|
||||
// ListByUserID returns every entitlement period history record owned by
|
||||
// userID.
|
||||
ListByUserID(ctx context.Context, userID common.UserID) ([]entitlement.PeriodRecord, error)
|
||||
|
||||
// Update replaces one stored entitlement period history record.
|
||||
Update(ctx context.Context, record entitlement.PeriodRecord) error
|
||||
}
|
||||
|
||||
// EntitlementSnapshotStore persists the read-optimized current entitlement
|
||||
// snapshot.
|
||||
type EntitlementSnapshotStore interface {
|
||||
// GetByUserID returns the current entitlement snapshot for userID.
|
||||
GetByUserID(ctx context.Context, userID common.UserID) (entitlement.CurrentSnapshot, error)
|
||||
|
||||
// Put stores the current entitlement snapshot for record.UserID.
|
||||
Put(ctx context.Context, record entitlement.CurrentSnapshot) error
|
||||
}
|
||||
|
||||
// GrantEntitlementInput stores one atomic transition from a current free
|
||||
// entitlement state to a current paid state.
|
||||
type GrantEntitlementInput struct {
|
||||
// ExpectedCurrentSnapshot stores the exact snapshot that must still be
|
||||
// current before the mutation commits.
|
||||
ExpectedCurrentSnapshot entitlement.CurrentSnapshot
|
||||
|
||||
// ExpectedCurrentRecord stores the current effective free period that must
|
||||
// still be current before the mutation commits.
|
||||
ExpectedCurrentRecord entitlement.PeriodRecord
|
||||
|
||||
// UpdatedCurrentRecord stores ExpectedCurrentRecord after the close metadata
|
||||
// is applied.
|
||||
UpdatedCurrentRecord entitlement.PeriodRecord
|
||||
|
||||
// NewRecord stores the new paid entitlement history segment.
|
||||
NewRecord entitlement.PeriodRecord
|
||||
|
||||
// NewSnapshot stores the new current effective entitlement snapshot.
|
||||
NewSnapshot entitlement.CurrentSnapshot
|
||||
}
|
||||
|
||||
// Validate reports whether GrantEntitlementInput is structurally complete.
|
||||
func (input GrantEntitlementInput) Validate() error {
|
||||
if err := input.ExpectedCurrentSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("grant entitlement input expected current snapshot: %w", err)
|
||||
}
|
||||
if err := input.ExpectedCurrentRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("grant entitlement input expected current record: %w", err)
|
||||
}
|
||||
if err := input.UpdatedCurrentRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("grant entitlement input updated current record: %w", err)
|
||||
}
|
||||
if err := input.NewRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("grant entitlement input new record: %w", err)
|
||||
}
|
||||
if err := input.NewSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("grant entitlement input new snapshot: %w", err)
|
||||
}
|
||||
if input.ExpectedCurrentSnapshot.UserID != input.ExpectedCurrentRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.UpdatedCurrentRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.NewRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.NewSnapshot.UserID {
|
||||
return fmt.Errorf("grant entitlement input all records must belong to the same user id")
|
||||
}
|
||||
if input.ExpectedCurrentRecord.RecordID != input.UpdatedCurrentRecord.RecordID {
|
||||
return fmt.Errorf("grant entitlement input updated current record must preserve record id")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtendEntitlementInput stores one atomic extension of a current finite paid
|
||||
// entitlement state.
|
||||
type ExtendEntitlementInput struct {
|
||||
// ExpectedCurrentSnapshot stores the exact snapshot that must still be
|
||||
// current before the mutation commits.
|
||||
ExpectedCurrentSnapshot entitlement.CurrentSnapshot
|
||||
|
||||
// NewRecord stores the appended entitlement history segment that extends the
|
||||
// current paid state.
|
||||
NewRecord entitlement.PeriodRecord
|
||||
|
||||
// NewSnapshot stores the replacement current effective entitlement snapshot.
|
||||
NewSnapshot entitlement.CurrentSnapshot
|
||||
}
|
||||
|
||||
// Validate reports whether ExtendEntitlementInput is structurally complete.
|
||||
func (input ExtendEntitlementInput) Validate() error {
|
||||
if err := input.ExpectedCurrentSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("extend entitlement input expected current snapshot: %w", err)
|
||||
}
|
||||
if err := input.NewRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("extend entitlement input new record: %w", err)
|
||||
}
|
||||
if err := input.NewSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("extend entitlement input new snapshot: %w", err)
|
||||
}
|
||||
if input.ExpectedCurrentSnapshot.UserID != input.NewRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.NewSnapshot.UserID {
|
||||
return fmt.Errorf("extend entitlement input all records must belong to the same user id")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeEntitlementInput stores one atomic transition from a current paid
|
||||
// entitlement state to a new free state.
|
||||
type RevokeEntitlementInput struct {
|
||||
// ExpectedCurrentSnapshot stores the exact snapshot that must still be
|
||||
// current before the mutation commits.
|
||||
ExpectedCurrentSnapshot entitlement.CurrentSnapshot
|
||||
|
||||
// ExpectedCurrentRecord stores the current effective paid period that must
|
||||
// still be current before the mutation commits.
|
||||
ExpectedCurrentRecord entitlement.PeriodRecord
|
||||
|
||||
// UpdatedCurrentRecord stores ExpectedCurrentRecord after the close metadata
|
||||
// is applied.
|
||||
UpdatedCurrentRecord entitlement.PeriodRecord
|
||||
|
||||
// NewRecord stores the newly created free entitlement period.
|
||||
NewRecord entitlement.PeriodRecord
|
||||
|
||||
// NewSnapshot stores the replacement current effective free snapshot.
|
||||
NewSnapshot entitlement.CurrentSnapshot
|
||||
}
|
||||
|
||||
// Validate reports whether RevokeEntitlementInput is structurally complete.
|
||||
func (input RevokeEntitlementInput) Validate() error {
|
||||
if err := input.ExpectedCurrentSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("revoke entitlement input expected current snapshot: %w", err)
|
||||
}
|
||||
if err := input.ExpectedCurrentRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("revoke entitlement input expected current record: %w", err)
|
||||
}
|
||||
if err := input.UpdatedCurrentRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("revoke entitlement input updated current record: %w", err)
|
||||
}
|
||||
if err := input.NewRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("revoke entitlement input new record: %w", err)
|
||||
}
|
||||
if err := input.NewSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("revoke entitlement input new snapshot: %w", err)
|
||||
}
|
||||
if input.ExpectedCurrentSnapshot.UserID != input.ExpectedCurrentRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.UpdatedCurrentRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.NewRecord.UserID ||
|
||||
input.ExpectedCurrentSnapshot.UserID != input.NewSnapshot.UserID {
|
||||
return fmt.Errorf("revoke entitlement input all records must belong to the same user id")
|
||||
}
|
||||
if input.ExpectedCurrentRecord.RecordID != input.UpdatedCurrentRecord.RecordID {
|
||||
return fmt.Errorf("revoke entitlement input updated current record must preserve record id")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RepairExpiredEntitlementInput stores one atomic lazy-repair transition from
|
||||
// an expired finite paid snapshot to a materialized free state.
|
||||
type RepairExpiredEntitlementInput struct {
|
||||
// ExpectedExpiredSnapshot stores the exact expired snapshot that must still
|
||||
// be current before the repair commits.
|
||||
ExpectedExpiredSnapshot entitlement.CurrentSnapshot
|
||||
|
||||
// NewRecord stores the newly created free entitlement period.
|
||||
NewRecord entitlement.PeriodRecord
|
||||
|
||||
// NewSnapshot stores the replacement current effective free snapshot.
|
||||
NewSnapshot entitlement.CurrentSnapshot
|
||||
}
|
||||
|
||||
// Validate reports whether RepairExpiredEntitlementInput is structurally
|
||||
// complete.
|
||||
func (input RepairExpiredEntitlementInput) Validate() error {
|
||||
if err := input.ExpectedExpiredSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("repair expired entitlement input expected expired snapshot: %w", err)
|
||||
}
|
||||
if err := input.NewRecord.Validate(); err != nil {
|
||||
return fmt.Errorf("repair expired entitlement input new record: %w", err)
|
||||
}
|
||||
if err := input.NewSnapshot.Validate(); err != nil {
|
||||
return fmt.Errorf("repair expired entitlement input new snapshot: %w", err)
|
||||
}
|
||||
if input.ExpectedExpiredSnapshot.UserID != input.NewRecord.UserID ||
|
||||
input.ExpectedExpiredSnapshot.UserID != input.NewSnapshot.UserID {
|
||||
return fmt.Errorf("repair expired entitlement input all records must belong to the same user id")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EntitlementLifecycleStore persists atomic entitlement timeline transitions
|
||||
// that must keep history and current snapshot consistent.
|
||||
type EntitlementLifecycleStore interface {
|
||||
// Grant atomically closes the current free period, creates a new paid
|
||||
// period, and replaces the current snapshot.
|
||||
Grant(ctx context.Context, input GrantEntitlementInput) error
|
||||
|
||||
// Extend atomically appends one paid-history segment and replaces the
|
||||
// current snapshot.
|
||||
Extend(ctx context.Context, input ExtendEntitlementInput) error
|
||||
|
||||
// Revoke atomically closes the current paid period, creates a new free
|
||||
// period, and replaces the current snapshot.
|
||||
Revoke(ctx context.Context, input RevokeEntitlementInput) error
|
||||
|
||||
// RepairExpired atomically replaces one expired finite paid snapshot with a
|
||||
// materialized free state.
|
||||
RepairExpired(ctx context.Context, input RepairExpiredEntitlementInput) error
|
||||
}
|
||||
Reference in New Issue
Block a user