Commit a236ca7
authored
refactor(audit): final DRY optimization and documentation pass (#425)
* docs: add comprehensive audit implementation analysis findings
Document findings from systematic analysis of async audit implementation
across 6 services. Key findings include:
- Payment-order service has full local audit implementation (DRY violation)
- Migration schema inconsistencies (record_id types, JSON storage types)
- Context key management duplication between models and audit package
- Redundant type aliases in multiple services
Prioritized recommendations for subsequent cleanup subtasks.
* feat(audit): add RecordUpdateManual helper for map-based updates
Add exported RecordUpdateManual function to the shared audit library
for use when GORM hooks cannot be used. This supports the pattern where
repositories use map-based updates with optimistic locking, which
bypasses GORM struct hooks.
The function accepts old and new entity values and records the UPDATE
operation in the audit trail, maintaining consistency with the hook-based
approach used for model updates.
* refactor(payment-order): migrate audit to shared library
Remove duplicated audit infrastructure from payment-order service
and use the shared audit library instead. This eliminates ~100 lines
of duplicated code including:
- Local AuditOutbox struct definition
- Local recordAudit function
- Local getUserIDFromContext function
- Local ErrNilTransaction error
The PaymentOrderEntity now implements the audit.Auditable interface
and uses audit.RecordCreate/RecordDelete for hook-based auditing.
The repository uses the new audit.RecordUpdateManual for its map-based
update pattern that bypasses GORM hooks.
This migration brings payment-order in line with other services
(tenant, party, financial-accounting) that already use the shared
audit library, and enables Kafka-first publishing with outbox fallback.
* refactor(tenant): remove redundant TenantAuditOutbox and TenantAuditLog types
Remove the locally-defined TenantAuditOutbox and TenantAuditLog structs
from the tenant service. These types were functionally identical to the
shared audit.AuditOutbox and audit.AuditLog types (both use string
RecordID to accommodate tenant's string IDs).
The shared types already support both UUID and string record IDs,
making these local definitions unnecessary duplication.
The service continues to use the existing type aliases (AuditOutbox,
systemUser) for backward compatibility with tests.
* test(payment-order): update audit tests for shared library types
Update audit tests to work with the shared audit library types:
- Change record_id column from UUID to VARCHAR(50) to match the shared
AuditOutbox struct which uses string to support both UUID and string IDs
- Change old_values/new_values columns from JSONB to TEXT to match the
shared struct and handle empty strings (JSONB rejects empty string "")
- Update assertions from pointer checks (!=nil) to string checks (Empty/NotEmpty)
- Update JSON unmarshal calls to use string values directly instead of
dereferencing pointers
These changes align the test infrastructure with the shared audit library
types while maintaining full test coverage of audit functionality.
* feat(audit): add adaptive polling and configurable worker options
Implement performance optimizations for the audit worker:
- Add functional options pattern (WithBatchSize, WithPollInterval,
WithMaxRetries, WithAdaptivePolling) for flexible worker configuration
- Implement adaptive polling that reduces database load during idle
periods by exponentially backing off poll interval when outbox is
empty, and immediately responding when entries arrive
- Add new metrics: batch_size histogram, poll_interval_seconds gauge,
and empty_polls_consecutive gauge for monitoring adaptive behavior
- Add processBatchWithCount internal method to support adaptive polling
decisions based on actual work performed
These changes are backward compatible - default behavior is unchanged
unless new options are explicitly used.
* refactor(audit): centralize status constants and error types
- Create status.go with exported status constants (StatusPending,
StatusProcessing, StatusFailed, StatusCompleted)
- Add operation constants (OperationInsert, OperationUpdate, OperationDelete)
- Add table name constants (TableAuditOutbox, TableAuditLog)
- Create errors.go consolidating all audit-related errors from hooks.go,
worker.go, and publisher.go into a single file
- Update worker.go, hooks.go, publisher.go to use centralized constants
- Update worker_test.go to use exported status constants
This improves code consistency by providing a single source of truth
for status values and error types used across the audit package and
services. Constants can now be used in SQL migrations and by consumers
of the audit package.
* refactor(audit): remove redundant type aliases and consolidate context helpers
- Remove AuditOutbox and AuditLog type aliases from service persistence
packages (tenant, party, financial-accounting, payment-order)
- Update all test files to use audit.AuditOutbox directly instead of
service-local aliases
- Consolidate getUserIDFromContext in shared/domain/models/base.go to
use audit.GetUserFromContext(), reducing code duplication
- Update SystemUser constant to use audit.DefaultAuditUser
This cleanup removes ~40 lines of redundant type alias definitions
across 4 services while maintaining backward compatibility. Tests now
reference the canonical shared audit types directly.
* docs(audit): add comprehensive documentation for async audit system
- Add package-level godoc in doc.go with architecture overview,
usage examples, and performance characteristics
- Enhance README.md with configuration options, adaptive polling
documentation, and troubleshooting guide
- Create operations/audit-monitoring.md with Prometheus metrics
reference, alert thresholds, Grafana queries, and failure modes
- Create development/audit-adding-new-service.md with step-by-step
guide including migration templates, entity implementation, and
test examples
The documentation covers the complete async audit system including
the dual-path Kafka/outbox architecture per ADR-0009.
* fix(party): update lifecycle tests to use shared audit package
Update lifecycle_integration_test.go to use audit.AuditOutbox from the
shared platform audit package instead of the removed service-specific
persistence.PartyAuditOutbox type.
* fix(audit): use TEXT instead of JSONB for audit outbox values
Change OldValues and NewValues columns in AuditOutbox and AuditLog
structs from JSONB to TEXT to match the migration compatibility fixes.
This prevents SQLSTATE 22P02 errors when tests run with GORM
auto-migration which creates columns based on struct definitions.
The JSONB type rejects empty strings as invalid JSON, but the audit
system may write empty strings for null values. TEXT columns are more
permissive and align with the migration schema changes.
* fix(payment-order): update test schemas to use TEXT for audit values
Update audit_outbox table creation in repository and integration tests
to use TEXT columns for old_values/new_values instead of JSONB. Also
change record_id from UUID to VARCHAR(50) to match shared audit infra.
This aligns with the shared audit package and migration compatibility
fixes that use TEXT to handle empty strings which JSONB rejects.
* docs(audit): align migration template with shared audit types
Update the audit adding new service guide to use TEXT columns and
VARCHAR(50) for record_id instead of JSONB/UUID. This aligns with
the shared audit package which uses TEXT to handle empty strings
that JSONB would reject as invalid JSON.
Also update the change_summary view to safely cast TEXT to JSONB
only when the content is valid JSON format.
* fix(audit): address CodeRabbit review feedback
- Align record_id type with shared package (VARCHAR(50) instead of UUID)
in party service test schemas for consistency
- Add validation to WithAdaptivePolling to swap inverted min/max intervals
- Add TODO for future improvement: use updated_at instead of created_at
for more accurate stuck entry detection (requires schema migration)
---------
Co-authored-by: Ben Coombs <[email protected]>1 parent fa860fd commit a236ca7
29 files changed
Lines changed: 1963 additions & 431 deletions
File tree
- docs
- development
- operations
- services
- financial-accounting
- adapters
- messaging
- persistence
- service
- party
- adapters/persistence
- service
- payment-order
- adapters/persistence
- service
- tenant/adapters/persistence
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
0 commit comments