Implementing Business Logic

Using Worker and Observer

This document explains how to write and use domain-level business logic in Baselines using two core primitives:

  • Worker — for one-time business operations
  • Observer — for continuous or reactive business state

If you are implementing custom business logic in the domain layer, this is the entry point.


Why Worker and Observer Exist

Baselines enforces a strict separation between UI, domain logic, and data access.

Business logic must:

  • be explicit
  • be testable
  • fail safely
  • not depend on UI or platform code

Worker and Observer provide a standardized way to express business intent while enforcing these rules.


When to Use Which

ScenarioUse
Fetch data onceWorker
Submit or mutate dataWorker
Trigger an actionWorker
Observe state over timeObserver
React to data changesObserver
Stream updates to UIObserver

Writing a Worker

A Worker represents a single business operation.

It:

  • runs once
  • returns a Result
  • safely captures failures
  • always reports success or failure explicitly

Important: Exception Handling

It is safe and intentional to throw exceptions inside a Worker.

Any exception thrown from doWork:

  • will not crash the app
  • is automatically captured
  • is returned as Result.Failure
  • is handled via onFailure { ... }

This allows you to use exceptions for clear control flow in domain logic without leaking crashes into the UI layer.

Example

kotlin
1// Domain layer
2@Inject
3class GetStartRoute(
4 private val checkAuthenticated: CheckAuthenticated,
5) : Worker<Unit, NavRoute>() {
6
7 override suspend fun doWork(params: Unit): NavRoute {
8 val authenticated = checkAuthenticated(Unit).getOrThrow()
9 return if (authenticated) {
10 AppNavRoutes.Home
11 } else {
12 AppNavRoutes.Auth
13 }
14 }
15}
16
17// Usage
18@Inject
19class StartRouteInitializer(
20 private val getStartRoute: GetStartRoute,
21) : AsyncInitializer {
22
23 override suspend fun init() {
24 getStartRoute(Unit)
25 .onSuccess { /* Assign the default start route */ }
26 .onFailure { /* Handle error state explicitly */ }
27 }
28}