Mutual Exclusion Rule Engine
Component Detail
Description
Pure Dart service that encodes the compatibility matrix for expense types. Given a set of currently selected types, returns the set of types that cannot be added. Implements the enum-based compatibility matrix described in the implementation notes, ensuring mileage and public transit cannot coexist.
mutual-exclusion-rule-engine
Summaries
The Mutual Exclusion Rule Engine enforces the company's reimbursement policy rules at the point of expense entry, preventing employees from submitting invalid combinations of expense types — such as claiming both mileage and public transit for the same trip. This directly reduces fraudulent or erroneous claims before they reach finance, cutting review costs and eliminating back-and-forth correction cycles. By automating policy enforcement at the UI level, the organisation reduces compliance risk, accelerates reimbursement approval cycles, and increases employee trust in the system's fairness and consistency. The financial benefit compounds as headcount scales.
This component has medium complexity and zero external dependencies, making it an excellent candidate for early delivery and independent testing. It can be developed and validated in isolation before the UI or repository layers are complete, de-risking the overall feature timeline. The primary delivery risk is correctness of the compatibility matrix — thorough unit test coverage across all type-pair permutations is mandatory before integration. QA should include edge cases such as empty selections and single-type selections.
No deployment infrastructure is required beyond the app bundle. Plan for a dedicated review session with the business to sign off on the matrix definition before development begins.
Implemented as a pure Dart service with no dependencies, this component encodes the expense type compatibility matrix as a const data structure and exposes four interfaces: getDisabledTypes, isCompatible, validateSelection, and getExclusionReason. The matrix should be represented as a symmetric Map
Because this is pure logic with no I/O, it is trivially unit-testable and should achieve 100% branch coverage. Avoid mutable state — all methods should be stateless functions over their inputs.
Responsibilities
- Encode the full compatibility matrix for all expense type pairs
- Compute the set of disabled types from a given selection
- Validate a proposed full selection for correctness
- Return human-readable exclusion reasons for UI hints
Interfaces
getDisabledTypes(Set<ExpenseType> selected) → Set<ExpenseType>
isCompatible(ExpenseType a, ExpenseType b) → bool
validateSelection(Set<ExpenseType> selected) → ValidationResult
getExclusionReason(ExpenseType type, Set<ExpenseType> selected) → String?
Relationships
Related Data Entities (2)
Data entities managed by this component