Skip to main content

EngineBuilder

Struct EngineBuilder 

Source
pub struct EngineBuilder<ES = (), SS = NoopSnapshotStore, OS = NoopOutboxStore, DS = NoopDeadlineStore, PR = NoopProcessRegistry> { /* private fields */ }
Expand description

Assembles engine infrastructure and produces an EngineContext.

Uses type-state to enforce that an event store is provided before build can be called. All other stores default to Noop implementations.

§Quick start

// Minimal — event store only, all others are Noop:
let ctx = EngineBuilder::new()
    .with_event_store(InMemoryEventStore::new())
    .build();

// Full infrastructure:
let ctx = EngineBuilder::new()
    .with_event_store(InMemoryEventStore::new())
    .with_snapshot_store(InMemorySnapshotStore::new())
    .with_outbox_store(InMemoryOutboxStore::new())
    .with_deadline_store(InMemoryDeadlineStore::new())
    .with_registry(InMemoryProcessRegistry::new())
    .register(Box::new(GpkeModule))
    .build();

Implementations§

Source§

impl<OS, DS, PR> EngineBuilder<(), NoopSnapshotStore, OS, DS, PR>

Source

pub fn with_stores(outbox_store: OS, deadline_store: DS, registry: PR) -> Self

Create a production-ready builder with explicit stores for outbox, deadline, and process registry.

This constructor is available in all build configurations including production binaries. It enforces that the three stores that can cause silent data loss (OutboxStore, DeadlineStore, ProcessRegistry) are provided explicitly — there is no Noop fallback.

NoopSnapshotStore is used as the snapshot default because it is safe for production: skipping snapshots means full replay, but no data loss. Override with with_snapshot_store to enable snapshot-accelerated replay.

Call with_event_store before build — the event store is required.

let ctx = EngineBuilder::with_stores(outbox, deadline, registry)
    .with_event_store(store.clone())
    .with_snapshot_store(InMemorySnapshotStore::new())
    .build();
Source§

impl<ES, SS, OS, DS, PR> EngineBuilder<ES, SS, OS, DS, PR>

Source

pub fn with_event_store<ES2: EventStore>( self, store: ES2, ) -> EngineBuilder<ES2, SS, OS, DS, PR>

Set the event store. Requiredbuild() is only available once this has been called with a type that implements EventStore.

Replaces any previously set event store (type-state transition).

Source

pub fn with_snapshot_store<SS2: SnapshotStore>( self, store: SS2, ) -> EngineBuilder<ES, SS2, OS, DS, PR>

Set the snapshot store (default: NoopSnapshotStore).

§Default: NoopSnapshotStore

Without calling this method the builder uses NoopSnapshotStore, which silently discards all snapshot writes and returns None for every snapshot read. The engine still functions correctly — every command handling call replays the full event log from the beginning instead of starting from a stored snapshot. For low-volume processes this is fine; for long-lived processes with many events the replay cost can become significant.

Enable snapshotting in production by providing a real SnapshotStore implementation (e.g. the SlateDB-backed store in makod). In tests, [InMemorySnapshotStore][crate::snapshot::InMemorySnapshotStore] is available behind the testing feature flag.

Note: Process::state_with_snapshot is a compile-time no-op when the snapshot store is NoopSnapshotStore — it never calls the store and always returns None, so no snapshot is ever saved or loaded.

Source

pub fn with_outbox_store<OS2: OutboxStore>( self, store: OS2, ) -> EngineBuilder<ES, SS, OS2, DS, PR>

Set the outbox store (default: NoopOutboxStore).

Source

pub fn with_deadline_store<DS2: DeadlineStore>( self, store: DS2, ) -> EngineBuilder<ES, SS, OS, DS2, PR>

Set the deadline store (default: NoopDeadlineStore).

Source

pub fn with_registry<PR2: ProcessRegistry>( self, registry: PR2, ) -> EngineBuilder<ES, SS, OS, DS, PR2>

Set the process registry (default: NoopProcessRegistry).

Source

pub fn with_dead_letter_sink(self, sink: impl DeadLetterSink) -> Self

Set the dead-letter sink (default: LogDeadLetterSink).

The dead-letter sink receives every message that cannot be routed to a workflow. The default LogDeadLetterSink emits tracing::warn! events, making rejections visible in log output without configuration.

Override with a persistent DLQ implementation in production:

use mako_engine::dead_letter::LogDeadLetterSink;

let ctx = EngineBuilder::new()
    .with_event_store(my_store)
    .with_dead_letter_sink(MyPersistentDlq::new())
    .build();
Source

pub fn with_profile_validator( self, validator: impl Fn(&str) -> bool + Send + Sync + 'static, ) -> Self

Register an edi-energy profile validator for startup profile checks.

The closure receives a message-type string (e.g. "UTILMD") and must return true if at least one active profile for that message type is registered for today’s date.

Wire this in makod using the edi-energy global registry:

use edi_energy::registry::ReleaseRegistry;

let today = time::OffsetDateTime::now_utc().date();
builder.with_profile_validator(move |msg_type| {
    ReleaseRegistry::global()
        .profiles_for_str(msg_type)
        .any(|p| match (p.valid_from(), p.valid_until()) {
            (Some(f), Some(u)) => f <= today && today <= u,
            (Some(f), None)    => f <= today,
            (None, _)          => true,
        })
})

Domain crates do not need to call this — they only declare profile_requirements.

Source

pub fn register(self, module: Box<dyn EngineModule>) -> Self

Register a domain module.

The module name becomes visible in EngineContext::registered_modules after build is called.

Source

pub fn with_deployment_roles(self, roles: DeploymentRoles) -> Self

Set the active DeploymentRoles for this engine instance.

Controls role-conditional PID registration in EngineModule::register_pids_with_roles.

The default is DeploymentRoles::all(), which registers every PID unconditionally — identical to the pre-role-aware behavior. Providing an explicit role set restricts role-conditional blocks to only the declared roles:

  • NB-only (DeploymentRoles::nb()): 19001/19002 route to gpke-konfiguration; WiM nMSB blocks are skipped.
  • nMSB-only (DeploymentRoles::nmsb()): 19001/19002 route to wim-geraeteubernahme; GPKE NB blocks are skipped.
  • NB + gMSB (DeploymentRoles::nb_msb()): most common Stadtwerke combination.
§Conflict guard

When two modules would register the same PID to different workflows, the engine panics during build. Set explicit roles to prevent both modules from activating the same PID simultaneously:

use mako_engine::marktrolle::DeploymentRoles;

let ctx = EngineBuilder::with_stores(outbox, deadline, registry)
    .with_event_store(store)
    .with_deployment_roles(DeploymentRoles::nb())  // only NB: GPKE gets 19001/19002
    .register(Box::new(GpkeModule))
    .register(Box::new(WimModule))  // nMSB block skipped — no conflict
    .build();
Source§

impl<ES, SS, OS, DS, PR> EngineBuilder<ES, SS, OS, DS, PR>

Source

pub fn build(self) -> EngineContext<ES, SS, OS, DS, PR>

Build the EngineContext.

Consumes the builder. All registered modules and configured stores are moved into the returned EngineContext.

This method is only available when ES implements EventStore. If you have not called with_event_store, this will not compile.

§Panics

Panics when any registered module returns Err from EngineModule::configure. The panic message includes the module name and the error string so the deployment failure is actionable.

Auto Trait Implementations§

§

impl<ES = (), SS = NoopSnapshotStore, OS = NoopOutboxStore, DS = NoopDeadlineStore, PR = NoopProcessRegistry> !RefUnwindSafe for EngineBuilder<ES, SS, OS, DS, PR>

§

impl<ES = (), SS = NoopSnapshotStore, OS = NoopOutboxStore, DS = NoopDeadlineStore, PR = NoopProcessRegistry> !Sync for EngineBuilder<ES, SS, OS, DS, PR>

§

impl<ES = (), SS = NoopSnapshotStore, OS = NoopOutboxStore, DS = NoopDeadlineStore, PR = NoopProcessRegistry> !UnwindSafe for EngineBuilder<ES, SS, OS, DS, PR>

§

impl<ES, SS, OS, DS, PR> Freeze for EngineBuilder<ES, SS, OS, DS, PR>
where ES: Freeze, SS: Freeze, OS: Freeze, DS: Freeze, PR: Freeze,

§

impl<ES, SS, OS, DS, PR> Send for EngineBuilder<ES, SS, OS, DS, PR>
where ES: Send, SS: Send, OS: Send, DS: Send, PR: Send,

§

impl<ES, SS, OS, DS, PR> Unpin for EngineBuilder<ES, SS, OS, DS, PR>
where ES: Unpin, SS: Unpin, OS: Unpin, DS: Unpin, PR: Unpin,

§

impl<ES, SS, OS, DS, PR> UnsafeUnpin for EngineBuilder<ES, SS, OS, DS, PR>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more