Skip to main content

ADR-002: Pulse vs Ploc — when to use each

Status

Accepted.

Context

CAF needs a single reactive primitive that is framework-agnostic, and a structured state container for screens or features. We had to decide whether to expose one primitive or both, and how to name and scope them.

Decision

We provide two layers in @c-a-f/core:

Pulse — single reactive value

  • What: A proxy-based reactive cell: holds one value and notifies subscribers when it changes. API: pulse(initialValue), .value, .subscribe(), .unsubscribe().
  • When to use: One reactive value (e.g. loading flag, current user ref, a single count).

Ploc — Presentation Logic Component

  • What: An abstract class built on top of Pulse. Holds structured state and exposes state, changeState(), subscribe(), unsubscribe(). Subclasses add methods that implement presentation logic.
  • When to use: A stateful "bloc" for a screen or feature: multiple related fields (e.g. { users, loading, error }), with logic that coordinates use cases and updates state.

Relationship: Ploc is implemented using Pulse. The core has one reactive engine (Pulse); Ploc composes it for the common case of "structured state + logic."

Consequences

Positive: One reactive primitive keeps the core small; Ploc gives a clear pattern; clear guidance ("single value → Pulse; structured state + logic → Ploc").

Negative: Two concepts to learn; Pulse is intentionally minimal (no built-in batching or devtools).