| Web / Mobile | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Authenticate workspace actor and load task plus pinned workflow version. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents the external caller from also owning the contract and failure behavior of Task API. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Two users edit same task version” to the external caller. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Web / Mobile boundary before extracting or scaling it. |
| Task API | Owns authentication, validation, quotas, deadlines, and routing. In this design it participates in: Validate requested fields/transition against role, current state, and expected version. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Web / Mobile from also owning the contract and failure behavior of Auth / Workspace. | Put protocol, authentication, quotas, and business transitions in one process. Acceptable at low scale, but it couples external traffic risk to domain releases. Here that simpler choice would couple “Dependency edge creates long cycle” to Web / Mobile. | Costs another hop, policy distribution, and a high-blast-radius fleet. Introduce it when many clients or services need one protocol and protection boundary. Measure the latency, saturation, lag, or mismatch at the Task API boundary before extracting or scaling it. |
| Auth / Workspace | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: For dependency edge, traverse bounded ancestors or use maintained topological metadata to reject cycle. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Task API from also owning the contract and failure behavior of Task Aggregate. | Embed policy in every caller. Rules then drift by deployment version and emergency changes require fleet-wide redeployment. Here that simpler choice would couple “Workflow changes while tasks active” to Task API. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Auth / Workspace boundary before extracting or scaling it. |
| Task Aggregate | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Conditionally update task and append immutable activity/outbox in one transaction. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Auth / Workspace from also owning the contract and failure behavior of Workflow Engine. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Recurring worker retries occurrence” to Auth / Workspace. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Task Aggregate boundary before extracting or scaling it. |
| Workflow Engine | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Index/search/notify asynchronously; recurring worker creates deduped occurrence IDs. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Task Aggregate from also owning the contract and failure behavior of Transition Policy. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Notification fan-out spams watchers” to Task Aggregate. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Workflow Engine boundary before extracting or scaling it. |
| Transition Policy | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Authenticate workspace actor and load task plus pinned workflow version. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Workflow Engine from also owning the contract and failure behavior of Dependency Graph. | Embed policy in every caller. Rules then drift by deployment version and emergency changes require fleet-wide redeployment. Here that simpler choice would couple “Two users edit same task version” to Workflow Engine. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Transition Policy boundary before extracting or scaling it. |
| Dependency Graph | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Validate requested fields/transition against role, current state, and expected version. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Transition Policy from also owning the contract and failure behavior of Cycle Detector. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Dependency edge creates long cycle” to Transition Policy. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Dependency Graph boundary before extracting or scaling it. |
| Cycle Detector | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: For dependency edge, traverse bounded ancestors or use maintained topological metadata to reject cycle. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Dependency Graph from also owning the contract and failure behavior of Task DB. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Workflow changes while tasks active” to Dependency Graph. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Cycle Detector boundary before extracting or scaling it. |
| Task DB | Owns authoritative versioned state or immutable evidence. In this design it participates in: Conditionally update task and append immutable activity/outbox in one transaction. | Defines the source of truth and commit boundary. It prevents Cycle Detector from also owning the contract and failure behavior of Activity Log. | Let each service persist its own interpretation. That removes one clear commit boundary and makes retries, support, and reconciliation ambiguous. Here that simpler choice would couple “Recurring worker retries occurrence” to Cycle Detector. | Costs schema migration, replication, backup, and shard ownership. Split storage only when access patterns, retention, or compliance boundaries materially differ. Measure the latency, saturation, lag, or mismatch at the Task DB boundary before extracting or scaling it. |
| Activity Log | Preserves durable ordering, replay, and consumer checkpoints. In this design it participates in: Index/search/notify asynchronously; recurring worker creates deduped occurrence IDs. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Task DB from also owning the contract and failure behavior of Comment Service. | Call every downstream consumer synchronously. That couples user latency and availability to analytics, notification, indexing, and audit dependencies. Here that simpler choice would couple “Notification fan-out spams watchers” to Task DB. | Costs eventual consistency, schema evolution, replay tooling, idempotent consumers, and lag operations. Earn it when bursts or downstream failure must not block acknowledgment. Measure the latency, saturation, lag, or mismatch at the Activity Log boundary before extracting or scaling it. |
| Comment Service | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Authenticate workspace actor and load task plus pinned workflow version. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Activity Log from also owning the contract and failure behavior of Search Index. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Two users edit same task version” to Activity Log. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Comment Service boundary before extracting or scaling it. |
| Search Index | Serves a low-latency, disposable projection with authoritative fallback. In this design it participates in: Validate requested fields/transition against role, current state, and expected version. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Comment Service from also owning the contract and failure behavior of Notification Router. | Read authoritative storage for every request. Simpler initially, but latency and source-of-truth load scale together and a hot key becomes a database incident. Here that simpler choice would couple “Dependency edge creates long cycle” to Comment Service. | Costs invalidation, staleness, stampede protection, and memory. Add or split it only after the workload (Millions tasks · collaborative updates · flexible workflows) proves repeated reads dominate. Measure the latency, saturation, lag, or mismatch at the Search Index boundary before extracting or scaling it. |
| Notification Router | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: For dependency edge, traverse bounded ancestors or use maintained topological metadata to reject cycle. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Search Index from also owning the contract and failure behavior of Recurring Task Worker. | Keep this behavior inside an adjacent service. Prefer that simpler option until independent scale, ownership, policy, or failure isolation is measurable. Here that simpler choice would couple “Workflow changes while tasks active” to Search Index. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Notification Router boundary before extracting or scaling it. |
| Recurring Task Worker | Executes idempotent asynchronous work under lease and retry policy. In this design it participates in: Conditionally update task and append immutable activity/outbox in one transaction. | Separates a distinct scale, ownership, policy, or failure boundary. It prevents Notification Router from also owning the contract and failure behavior of Audit / Export. | Perform the work before acknowledging the caller. This gives simpler consistency but unbounded tail latency and fragile retries. Here that simpler choice would couple “Recurring worker retries occurrence” to Notification Router. | Costs leases, checkpoints, retries, DLQs, and lag operations. Separate once execution time or burstiness no longer fits the request deadline. Measure the latency, saturation, lag, or mismatch at the Recurring Task Worker boundary before extracting or scaling it. |
| Audit / Export | Owns one bounded domain decision, transition, or operational capability. In this design it participates in: Index/search/notify asynchronously; recurring worker creates deduped occurrence IDs. | Proves correctness and supports controlled recovery. It prevents Recurring Task Worker from also owning the contract and failure behavior of operators and repair tooling. | Infer correctness from request logs. Logs do not prove durable business state, detect silent drift, or safely repair ambiguous outcomes. Here that simpler choice would couple “Notification fan-out spams watchers” to Recurring Task Worker. | Costs an interface, deployment, telemetry, ownership, and failure mode. Keep it in-process first; extract when independent scale or reliability pays for the boundary. Measure the latency, saturation, lag, or mismatch at the Audit / Export boundary before extracting or scaling it. |