Documentation
¶
Index ¶
- Variables
- type Account
- type AccountRepository
- type AccountService
- type AccountType
- type AdminAuditRepository
- type AdminService
- type AdminTenantRepository
- type AdminUserRepository
- type AuditAction
- type AuditLog
- type AuditRepository
- type AuthRepository
- type AuthService
- type CachedResponse
- type Category
- type CategoryRepository
- type CategoryService
- type CategoryType
- type Claims
- type CreateAccountInput
- type CreateAuditLogInput
- type CreateCategoryInput
- type CreateOTPRequestInput
- type CreateTenantInput
- type CreateTransactionInput
- type CreateUserInput
- type IdempotencyStore
- type ListAuditLogsParams
- type ListTransactionsParams
- type Mailer
- type OTPRequest
- type Role
- type Tenant
- type TenantPlan
- type TenantRepository
- type TenantService
- type TokenPair
- type Transaction
- type TransactionRepository
- type TransactionService
- type TransactionType
- type UpdateAccountInput
- type UpdateCategoryInput
- type UpdateTenantInput
- type UpdateTransactionInput
- type UpdateUserInput
- type User
- type UserRepository
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotFound is returned when a requested resource does not exist or has been soft-deleted. ErrNotFound = errors.New("not found") // ErrForbidden is returned when the caller lacks permission for a resource. ErrForbidden = errors.New("forbidden") // ErrConflict is returned when an operation would violate a uniqueness constraint. ErrConflict = errors.New("conflict") // ErrInvalidInput is returned when request validation fails. ErrInvalidInput = errors.New("invalid input") // ErrInvalidOTP is returned when an OTP code is wrong, expired, or already used. ErrInvalidOTP = errors.New("invalid or expired OTP") // ErrOTPRateLimited is returned when too many OTP requests are made. ErrOTPRateLimited = errors.New("OTP rate limit exceeded") ErrUnauthorized = errors.New("unauthorized") // ErrTokenExpired is returned when the PASETO token has expired. ErrTokenExpired = errors.New("token expired") )
Sentinel errors — all in internal/domain/errors.go
Functions ¶
This section is empty.
Types ¶
type Account ¶
type Account struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
ID string `json:"id"`
TenantID string `json:"tenant_id"`
UserID string `json:"user_id"`
Name string `json:"name"`
Type AccountType `json:"type"`
Currency string `json:"currency"` // ISO 4217 code (e.g. "USD")
BalanceCents int64 `json:"balance_cents"` // Always in cents; never float
}
Account represents a financial account owned by a user within a household (tenant). All balances are stored in cents (int64) to ensure precision.
type AccountRepository ¶
type AccountRepository interface {
// Create persists a new account for the specified tenant.
Create(ctx context.Context, tenantID string, input CreateAccountInput) (*Account, error)
// GetByID retrieves a specific account by its ID and tenant ID.
GetByID(ctx context.Context, tenantID, id string) (*Account, error)
// ListByTenant returns all active accounts for the given tenant.
ListByTenant(ctx context.Context, tenantID string) ([]Account, error)
// ListByUser returns all accounts associated with a specific user within a tenant.
ListByUser(ctx context.Context, tenantID, userID string) ([]Account, error)
// Update modifies an existing account's metadata.
Update(ctx context.Context, tenantID, id string, input UpdateAccountInput) (*Account, error)
// UpdateBalance updates the balance of an account. This should be used strictly
// through service logic to ensure consistency with transactions.
UpdateBalance(ctx context.Context, tenantID, id string, newBalanceCents int64) error
// Delete performs a soft delete on the specified account.
Delete(ctx context.Context, tenantID, id string) error
}
AccountRepository defines the persistence operations for financial accounts. It ensures that all data is isolated by tenant.
type AccountService ¶
type AccountService interface {
// Create persists a new account after validating the user belongs to the tenant.
Create(ctx context.Context, tenantID string, input CreateAccountInput) (*Account, error)
// GetByID retrieves an account by ID with a tenant ownership guard.
GetByID(ctx context.Context, tenantID, id string) (*Account, error)
// ListByTenant returns all active accounts for the given household.
ListByTenant(ctx context.Context, tenantID string) ([]Account, error)
// ListByUser returns all accounts associated with a specific user within a tenant.
ListByUser(ctx context.Context, tenantID, userID string) ([]Account, error)
// Update modifies an existing account's metadata and records an audit log.
Update(ctx context.Context, tenantID, id string, input UpdateAccountInput) (*Account, error)
// Delete performs a soft-delete on the account and records an audit log.
Delete(ctx context.Context, tenantID, id string) error
}
AccountService defines the business-logic contract for account management.
type AccountType ¶
type AccountType string
AccountType mirrors the database enum for financial account types.
const ( // AccountTypeChecking representing a standard checking account. AccountTypeChecking AccountType = "checking" // AccountTypeSavings representing a savings or high-yield account. AccountTypeSavings AccountType = "savings" // AccountTypeCreditCard representing a credit card account. AccountTypeCreditCard AccountType = "credit_card" // AccountTypeInvestment representing an investment or brokerage account. AccountTypeInvestment AccountType = "investment" )
type AdminAuditRepository ¶
type AdminAuditRepository interface {
// ListAll returns audit logs across all tenants with optional filters.
ListAll(ctx context.Context, params ListAuditLogsParams) ([]AuditLog, error)
}
AdminAuditRepository defines global audit log queries without tenant isolation. This is used for system-wide compliance and debugging.
type AdminService ¶
type AdminService interface {
// ListAllTenants returns all tenants in the system, with an option to include soft-deleted ones.
ListAllTenants(ctx context.Context, withDeleted bool) ([]Tenant, error)
// GetTenantByID retrieves a tenant by its ID without tenant isolation.
GetTenantByID(ctx context.Context, id string) (*Tenant, error)
// UpdateTenantPlan changes the subscription plan for a tenant.
UpdateTenantPlan(ctx context.Context, id string, plan TenantPlan) (*Tenant, error)
// SuspendTenant performs a soft-delete on the tenant, blocking all user access.
SuspendTenant(ctx context.Context, id string) error
// RestoreTenant reverses a soft-delete on the tenant, restoring user access.
RestoreTenant(ctx context.Context, id string) error
// HardDeleteTenant permanently deletes a tenant and all associated data after confirmation.
HardDeleteTenant(ctx context.Context, id, confirmationToken string) error
// ListAllUsers returns all users across the system without tenant isolation.
ListAllUsers(ctx context.Context) ([]User, error)
// GetUserByID retrieves a user by ID without tenant isolation.
GetUserByID(ctx context.Context, id string) (*User, error)
// ForceDeleteUser permanently deletes a user record without tenant isolation.
ForceDeleteUser(ctx context.Context, id string) error
// ListAuditLogs returns audit logs across all tenants with optional filters, without tenant isolation.
ListAuditLogs(ctx context.Context, params ListAuditLogsParams) ([]AuditLog, error)
}
AdminService defines the business-logic contract for cross-tenant sysadmin operations.
type AdminTenantRepository ¶
type AdminTenantRepository interface {
// ListAll returns every tenant in the system, including soft-deleted ones when withDeleted is true.
ListAll(ctx context.Context, withDeleted bool) ([]Tenant, error)
// GetByID retrieves a tenant without a tenant_id guard (sysadmin bypass).
GetByID(ctx context.Context, id string) (*Tenant, error)
// UpdatePlan changes a tenant's subscription plan.
UpdatePlan(ctx context.Context, id string, plan TenantPlan) (*Tenant, error)
// Suspend soft-deletes a tenant, blocking all logins for its users.
Suspend(ctx context.Context, id string) error
// Restore reverses a soft-delete on a tenant.
Restore(ctx context.Context, id string) error
// HardDelete permanently removes a tenant and all associated data.
// WARNING: This is irreversible and will delete all household data via cascade.
// Must only be called after explicit confirmation.
HardDelete(ctx context.Context, id string) error
}
AdminTenantRepository defines cross-tenant operations for sysadmin use only. These methods bypass the standard tenant_id isolation for system-wide management.
type AdminUserRepository ¶
type AdminUserRepository interface {
// ListAll returns every user in the system regardless of tenant.
ListAll(ctx context.Context) ([]User, error)
// GetByID retrieves a user without a tenant_id guard.
GetByID(ctx context.Context, id string) (*User, error)
// ForceDelete hard-deletes a user record.
// WARNING: This is irreversible and should be used with extreme caution.
ForceDelete(ctx context.Context, id string) error
}
AdminUserRepository defines cross-tenant user operations for sysadmin use only. These methods allow management of users across the entire system.
type AuditAction ¶
type AuditAction string
AuditAction mirrors the database enum for audit actions.
const ( // AuditActionCreate representing many create operations. AuditActionCreate AuditAction = "create" // AuditActionUpdate representing many update operations. AuditActionUpdate AuditAction = "update" // AuditActionSoftDelete representing soft delete operations. AuditActionSoftDelete AuditAction = "soft_delete" // AuditActionRestore representing restore operations. AuditActionRestore AuditAction = "restore" // AuditActionLogin representing login operations. AuditActionLogin AuditAction = "login" // AuditActionLoginFailed representing login failure operations. AuditActionLoginFailed AuditAction = "login_failed" // AuditActionOTPRequested representing OTP request operations. AuditActionOTPRequested AuditAction = "otp_requested" // AuditActionOTPVerified representing OTP verification operations. AuditActionOTPVerified AuditAction = "otp_verified" )
type AuditLog ¶
type AuditLog struct {
CreatedAt time.Time `json:"created_at"`
ID string `json:"id"`
TenantID string `json:"tenant_id"`
ActorID string `json:"actor_id"` // User ULID or "SYSTEM" for automated actions
EntityType string `json:"entity_type"`
EntityID string `json:"entity_id"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
ActorRole Role `json:"actor_role"`
Action AuditAction `json:"action"`
OldValues []byte `json:"old_values,omitempty"` // JSON snapshot before change
NewValues []byte `json:"new_values,omitempty"` // JSON snapshot after change
}
AuditLog is an immutable record of a significant system event. It provides a tamper-evident trace of actor actions, IPs, and state changes.
type AuditRepository ¶
type AuditRepository interface {
// Create appends a new audit log entry.
Create(ctx context.Context, input CreateAuditLogInput) (*AuditLog, error)
// ListByTenant returns audit logs for a specific tenant with optional filters.
ListByTenant(ctx context.Context, tenantID string, params ListAuditLogsParams) ([]AuditLog, error)
// ListByEntity returns audit logs for a specific entity (e.g. a single transaction).
ListByEntity(ctx context.Context, tenantID, entityType, entityID string) ([]AuditLog, error)
}
AuditRepository defines persistence operations for audit logs. Audit logs are append-only — no update or delete methods are provided.
type AuthRepository ¶
type AuthRepository interface {
// CreateOTPRequest persists a new OTP challenge in the database.
CreateOTPRequest(ctx context.Context, input CreateOTPRequestInput) (*OTPRequest, error)
// GetActiveOTPRequest retrieves the most recent unused, non-expired OTP for the given email.
// Returns domain.ErrInvalidOTP if no valid request is found.
GetActiveOTPRequest(ctx context.Context, email string) (*OTPRequest, error)
// MarkOTPUsed marks the given OTP request as consumed to prevent reuse.
MarkOTPUsed(ctx context.Context, id string) error
// DeleteExpiredOTPRequests removes all expired OTP rows from the database.
// This is typically called by a periodic cleanup job.
DeleteExpiredOTPRequests(ctx context.Context) error
}
AuthRepository defines persistence operations for OTP challenges and authentication state. It decouples the auth service from specific database implementations.
type AuthService ¶
type AuthService interface {
// RequestOTP validates the email, generates an OTP, persists it, and mails
// the code to the user. Returns ErrNotFound if the user does not exist.
RequestOTP(ctx context.Context, email string) error
// VerifyOTP validates the OTP code for the given email. On success it marks
// the OTP as used, updates the user's last-login timestamp, records an audit
// log, and returns a fresh PASETO token pair.
VerifyOTP(ctx context.Context, email, code string) (*TokenPair, error)
// RefreshToken validates an existing refresh token and returns a new token
// pair with a refreshed expiry.
RefreshToken(ctx context.Context, refreshToken string) (*TokenPair, error)
}
AuthService defines the business-logic contract for the OTP auth flow.
type CachedResponse ¶
CachedResponse represents a stored HTTP response.
type Category ¶
type Category struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
ID string `json:"id"`
TenantID string `json:"tenant_id"`
ParentID string `json:"parent_id"` // Empty string means root category
Name string `json:"name"`
Icon string `json:"icon"` // Optional emoji or icon identifier
Color string `json:"color"` // Optional hex color, e.g. "#FF5733"
Type CategoryType `json:"type"`
}
Category is a tenant-scoped label for classifying transactions. It supports one level of parent-child hierarchy.
type CategoryRepository ¶
type CategoryRepository interface {
// Create persists a new category for the specified tenant.
Create(ctx context.Context, tenantID string, input CreateCategoryInput) (*Category, error)
// GetByID retrieves a specific category by its ID and tenant ID.
GetByID(ctx context.Context, tenantID, id string) (*Category, error)
// ListByTenant returns all active categories for the given tenant.
ListByTenant(ctx context.Context, tenantID string) ([]Category, error)
// ListChildren returns all subcategories for a given parent within a tenant.
ListChildren(ctx context.Context, tenantID, parentID string) ([]Category, error)
// Update modifies an existing category's metadata.
Update(ctx context.Context, tenantID, id string, input UpdateCategoryInput) (*Category, error)
// Delete performs a soft delete on the specified category.
Delete(ctx context.Context, tenantID, id string) error
}
CategoryRepository defines persistence operations for categories. It ensures that all data is isolated by tenant.
type CategoryService ¶
type CategoryService interface {
// Create persists a new category with hierarchy depth validation.
Create(ctx context.Context, tenantID string, input CreateCategoryInput) (*Category, error)
// GetByID retrieves a specific category by its ID and tenant ID.
GetByID(ctx context.Context, tenantID, id string) (*Category, error)
// ListByTenant returns all categories for the given tenant.
ListByTenant(ctx context.Context, tenantID string) ([]Category, error)
// ListChildren returns all subcategories for a given parent within a tenant.
ListChildren(ctx context.Context, tenantID, parentID string) ([]Category, error)
// Update modifies an existing category's metadata and writes to the audit trail.
Update(ctx context.Context, tenantID, id string, input UpdateCategoryInput) (*Category, error)
// Delete performs a soft-delete and writes to the audit trail.
Delete(ctx context.Context, tenantID, id string) error
}
CategoryService defines the business-logic contract for category management.
type CategoryType ¶
type CategoryType string
CategoryType mirrors the database enum for transaction categories.
const ( // CategoryTypeIncome representing income transactions (e.g. salary). CategoryTypeIncome CategoryType = "income" // CategoryTypeExpense representing expense transactions (e.g. groceries). CategoryTypeExpense CategoryType = "expense" // CategoryTypeTransfer representing transfers between accounts. CategoryTypeTransfer CategoryType = "transfer" )
type Claims ¶
type Claims struct {
IssuedAt time.Time `json:"issued_at"`
ExpiresAt time.Time `json:"expires_at"`
UserID string `json:"user_id"`
TenantID string `json:"tenant_id"`
Role Role `json:"role"`
}
Claims holds the data encoded in a PASETO token. It contains identification for the user and their tenant, as well as their role and metadata.
type CreateAccountInput ¶
type CreateAccountInput struct {
UserID string `validate:"required"`
Name string `validate:"required,min=1,max=100"`
Type AccountType `validate:"required,oneof=checking savings credit_card investment"`
Currency string `validate:"required,len=3"`
InitialCents int64 `validate:"required"`
}
CreateAccountInput is the value object used for creating a new account.
type CreateAuditLogInput ¶
type CreateAuditLogInput struct {
TenantID string `validate:"required"`
ActorID string `validate:"required"`
Action AuditAction `validate:"required"`
EntityType string `validate:"required"`
EntityID string `validate:"omitempty"`
IPAddress string `validate:"omitempty"`
UserAgent string `validate:"omitempty"`
ActorRole Role `validate:"required"`
OldValues []byte `json:"-"`
NewValues []byte `json:"-"`
}
CreateAuditLogInput is the value object used for recording a new audit event.
type CreateCategoryInput ¶
type CreateCategoryInput struct {
ParentID string `validate:"omitempty"`
Name string `validate:"required,min=1,max=100"`
Icon string `validate:"omitempty,max=10"`
Color string `validate:"omitempty,hexcolor"`
Type CategoryType `validate:"required,oneof=income expense transfer"`
}
CreateCategoryInput is the value object used for creating a new category.
type CreateOTPRequestInput ¶
type CreateOTPRequestInput struct {
ExpiresAt time.Time `validate:"required"`
Email string `validate:"required,email"`
CodeHash string `validate:"required"`
}
CreateOTPRequestInput is the value object used to create a new OTP challenge.
type CreateTenantInput ¶
type CreateTenantInput struct {
Name string `validate:"required,min=2,max=100"`
}
CreateTenantInput holds the data required to create a new household.
type CreateTransactionInput ¶
type CreateTransactionInput struct {
OccurredAt time.Time `validate:"required"`
AccountID string `validate:"required"`
CategoryID string `validate:"required"`
UserID string `validate:"required"`
Description string `validate:"required,min=1,max=255"`
MasterPurchaseID string `validate:"omitempty"`
Type TransactionType `validate:"required,oneof=income expense transfer"`
AmountCents int64 `validate:"required,gt=0"`
}
CreateTransactionInput is the value object used for creating a new transaction.
type CreateUserInput ¶
type CreateUserInput struct {
TenantID string `json:"tenant_id" validate:"required"`
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required,min=2,max=100"`
Role Role `json:"role" validate:"required"`
}
CreateUserInput holds the data required to enroll a new user.
type IdempotencyStore ¶
type IdempotencyStore interface {
// Get retrieves a cached response for the given key, if it exists and is not expired.
Get(ctx context.Context, key string) (*CachedResponse, error)
// SetLocked attempts to acquire a lock for the given key. It returns true if the lock was acquired,
SetLocked(ctx context.Context, key string, ttl time.Duration) (bool, error)
// SetResponse stores the response for the given key with a TTL, and releases any lock.
SetResponse(ctx context.Context, key string, resp CachedResponse, ttl time.Duration) error
}
IdempotencyStore defines the contract for storing idempotency keys and responses.
type ListAuditLogsParams ¶
type ListAuditLogsParams struct {
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
EntityType string `json:"entity_type,omitempty"`
EntityID string `json:"entity_id,omitempty"`
ActorID string `json:"actor_id,omitempty"`
Action AuditAction `json:"action,omitempty"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
ListAuditLogsParams defines the filters and pagination for querying audit trails.
type ListTransactionsParams ¶
type ListTransactionsParams struct {
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
AccountID string `json:"account_id,omitempty"`
CategoryID string `json:"category_id,omitempty"`
Type TransactionType `json:"type,omitempty"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
ListTransactionsParams defines the filters and pagination for listing transactions.
type Mailer ¶
type Mailer interface {
// SendOTP sends a one-time password code to the given email address.
SendOTP(ctx context.Context, to, code string) error
}
Mailer defines the contract for sending application emails. Implementations: SMTPMailer (production), NoopMailer (tests).
type OTPRequest ¶
type OTPRequest struct {
ExpiresAt time.Time `json:"expires_at"`
CreatedAt time.Time `json:"created_at"`
ID string `json:"id"`
Email string `json:"email"`
CodeHash string `json:"-"` // bcrypt hash of the 6-digit code, never serialized
Used bool `json:"used"`
}
OTPRequest represents a pending or consumed OTP challenge. It tracks the email associated with the request, the bcrypt hash of the 6-digit code, whether it has been used, and its expiration time.
type Role ¶
type Role string
Role represents a user's role within a household/tenant.
type Tenant ¶
type Tenant struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
ID string
Name string
Plan TenantPlan
}
Tenant represents a household, the root of the multi-tenancy hierarchy.
type TenantPlan ¶
type TenantPlan string
TenantPlan represents the subscription tier of a household.
const ( // TenantPlanFree is the default restricted tier. TenantPlanFree TenantPlan = "free" // TenantPlanBasic is the standard paid tier. TenantPlanBasic TenantPlan = "basic" // TenantPlanPremium is the highest tier with all features. TenantPlanPremium TenantPlan = "premium" )
type TenantRepository ¶
type TenantRepository interface {
// Create persists a new tenant.
Create(ctx context.Context, input CreateTenantInput) (*Tenant, error)
// GetByID retrieves a tenant by its unique identifier.
GetByID(ctx context.Context, id string) (*Tenant, error)
// List returns all active (non-deleted) tenants.
List(ctx context.Context) ([]Tenant, error)
// Update modifies an existing tenant's attributes.
Update(ctx context.Context, id string, input UpdateTenantInput) (*Tenant, error)
// Delete performs a soft-delete on the tenant.
Delete(ctx context.Context, id string) error
}
TenantRepository defines the persistence contract for Tenant entities. These operations are typically restricted to the sysadmin role.
type TenantService ¶
type TenantService interface {
// Create persists a new tenant and records an audit log.
Create(ctx context.Context, input CreateTenantInput) (*Tenant, error)
// GetByID retrieves a tenant by its unique identifier.
GetByID(ctx context.Context, id string) (*Tenant, error)
// List returns all active (non-deleted) tenants. Restricted to sysadmins.
List(ctx context.Context) ([]Tenant, error)
// Update modifies an existing tenant's attributes and records an audit log.
Update(ctx context.Context, id string, input UpdateTenantInput) (*Tenant, error)
// Delete performs a soft-delete on the tenant and records an audit log.
Delete(ctx context.Context, id string) error
// InviteUser creates a new user within the context of a tenant.
InviteUser(ctx context.Context, tenantID string, input CreateUserInput) (*User, error)
}
TenantService defines the business-logic contract for tenant management.
type TokenPair ¶
type TokenPair struct {
ExpiresAt time.Time `json:"expires_at"`
AccessToken string `json:"access_token"` //nolint:gosec
RefreshToken string `json:"refresh_token"` //nolint:gosec
}
TokenPair holds both tokens returned after successful OTP verification.
type Transaction ¶
type Transaction struct {
OccurredAt time.Time `json:"occurred_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
ID string `json:"id"`
TenantID string `json:"tenant_id"`
AccountID string `json:"account_id"`
CategoryID string `json:"category_id"`
UserID string `json:"user_id"`
MasterPurchaseID string `json:"master_purchase_id,omitempty"` // Empty for regular transactions; set in Phase 2
Description string `json:"description"`
Type TransactionType `json:"type"`
AmountCents int64 `json:"amount_cents"` // Always in cents; positive value; type determines direction
}
Transaction is a single financial event on an account. All amounts are stored in cents (int64) to ensure precision.
type TransactionRepository ¶
type TransactionRepository interface {
// Create persists a new transaction for the specified tenant.
Create(ctx context.Context, tenantID string, input CreateTransactionInput) (*Transaction, error)
// GetByID retrieves a specific transaction by its ID and tenant ID.
GetByID(ctx context.Context, tenantID, id string) (*Transaction, error)
// List returns a list of transactions matching the specified filters.
List(ctx context.Context, tenantID string, params ListTransactionsParams) ([]Transaction, error)
// Update modifies an existing transaction's metadata.
Update(ctx context.Context, tenantID, id string, input UpdateTransactionInput) (*Transaction, error)
// Delete performs a soft delete on the specified transaction.
Delete(ctx context.Context, tenantID, id string) error
}
TransactionRepository defines persistence operations for transactions. It ensures that all data is isolated by tenant.
type TransactionService ¶
type TransactionService interface {
// Create persists a transaction and updates the account balance.
Create(ctx context.Context, tenantID string, input CreateTransactionInput) (*Transaction, error)
// GetByID retrieves a transaction by ID with tenant isolation.
GetByID(ctx context.Context, tenantID, id string) (*Transaction, error)
// List returns transactions matching filters for the household.
List(ctx context.Context, tenantID string, params ListTransactionsParams) ([]Transaction, error)
// Update modifies a transaction and adjusts balance if AmountCents changed.
Update(ctx context.Context, tenantID, id string, input UpdateTransactionInput) (*Transaction, error)
// Delete reverts the balance impact and soft-deletes the transaction.
Delete(ctx context.Context, tenantID, id string) error
}
TransactionService defines the business-logic contract for transaction management.
type TransactionType ¶
type TransactionType string
TransactionType mirrors the database enum for transaction types.
const ( // TransactionTypeIncome representing income transactions (e.g. salary). TransactionTypeIncome TransactionType = "income" // TransactionTypeExpense representing expense transactions (e.g. groceries). TransactionTypeExpense TransactionType = "expense" // TransactionTypeTransfer representing transfers between accounts. TransactionTypeTransfer TransactionType = "transfer" )
type UpdateAccountInput ¶
type UpdateAccountInput struct {
Name *string `validate:"omitempty,min=1,max=100"`
Currency *string `validate:"omitempty,len=3"`
}
UpdateAccountInput is the value object used for updating an existing account.
type UpdateCategoryInput ¶
type UpdateCategoryInput struct {
Name *string `validate:"omitempty,min=1,max=100"`
Icon *string `validate:"omitempty,max=10"`
Color *string `validate:"omitempty,hexcolor"`
}
UpdateCategoryInput is the value object used for updating an existing category.
type UpdateTenantInput ¶
type UpdateTenantInput struct {
Name *string `validate:"omitempty,min=2,max=100"`
Plan *TenantPlan `validate:"omitempty"`
}
UpdateTenantInput holds the data required to update an existing household.
type UpdateTransactionInput ¶
type UpdateTransactionInput struct {
OccurredAt *time.Time `validate:"omitempty"`
CategoryID *string `validate:"omitempty"`
Description *string `validate:"omitempty,min=1,max=255"`
AmountCents *int64 `validate:"omitempty,gt=0"`
}
UpdateTransactionInput is the value object used for updating an existing transaction.
type UpdateUserInput ¶
type UpdateUserInput struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=2,max=100"`
Role *Role `json:"role,omitempty" validate:"omitempty"`
}
UpdateUserInput holds the data that can be updated for a user.
type User ¶
type User struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
LastLoginAt *time.Time
ID string
TenantID string
Email string
Name string
Role Role
}
User represents a person belonging to a Tenant (household). All operational field accesses must be isolated by TenantID.
type UserRepository ¶
type UserRepository interface {
// Create persists a new user within a tenant.
Create(ctx context.Context, input CreateUserInput) (*User, error)
// GetByID retrieves a user by ID, scoped to a specific tenant.
GetByID(ctx context.Context, tenantID, id string) (*User, error)
// GetByEmail retrieves a user by their unique email across all tenants.
// Used primarily during the authentication flow.
GetByEmail(ctx context.Context, email string) (*User, error)
// ListByTenant returns all active users belonging to a household.
ListByTenant(ctx context.Context, tenantID string) ([]User, error)
// Update modifies a user's attributes within their tenant.
Update(ctx context.Context, tenantID, id string, input UpdateUserInput) (*User, error)
// UpdateLastLogin updates the login timestamp for a user.
UpdateLastLogin(ctx context.Context, id string) error
// Delete performs a soft-delete on a user.
Delete(ctx context.Context, tenantID, id string) error
}
UserRepository defines the persistence contract for User entities. Every method (except GetByEmail for login) MUST enforce TenantID isolation.