Skip to content

Repository files navigation

Portia

Portia is a cohesive application framework for .NET 10. It carries an operation from its entry point through authorization and domain decisions, then runs the durable work that follows.

ASP.NET Core already does HTTP well. Portia is the application model behind HTTP—and behind RPC, queues, notices, schedules, and local calls. A request keeps the same handler, actor, authorization, causation, result, and telemetry semantics regardless of how it arrives.

HTTP / RPC / queue / schedule / local
                    │
                    ▼
 request → authorization → behaviors → guards → handler → aggregate → Fitz event log
                                                           │
                                           ┌───────────────┴───────────────┐
                                           ▼                               ▼
                                projector + checkpoint              reactor + effect
                                           │
                                           ▼
                           Cassie / PostgreSQL / Snowflake / ...

The unit of value is not command → handler → route. It is the complete path from intent to durable consequence.

What Portia provides

  • One application operation, many entry points. A handler is selected once and can be reached locally or through an explicitly allowed transport without transport logic entering the handler.
  • One execution context. Actor identity, authorization, correlation, causation, expected failures, tracing, and metrics follow the request across process and delivery boundaries.
  • Event-sourced decisions. Aggregates are ordinary application objects. Portia hydrates and saves their events with optimistic concurrency and stable event identities.
  • Durable consequences. Projectors atomically commit read-model changes with their checkpoints. Reactors run external effects at least once with preserved event causation. Failed work never silently advances progress.
  • One composition, independent deployments. An API and its workers share application setup but can be deployed and scaled separately. Workers support global, per-tenant, and fleet-coordinated ownership.
  • Compile-time wiring. Registration, transport descriptors, HTTP binding, and JSON roots are source-generated and checked by Portia analyzers. The supported runtime path uses no assembly scanning or reflection and is exercised as a packed NativeAOT application in CI.
  • Reusable request preflight. Scoped asynchronous guards run at the unary handler boundary, can target one request or an application-owned request family, and short-circuit with the ordinary Result contract.

A vertical slice

A request declares its stable identity and the transports the application permits:

[RequestRoute("banking", "accounts", "*", "deposit")]
[Discriminator("accounts.deposit")]
public sealed record Deposit(Uuid AccountId, int Amount)
    : IRequest, ICallable, IQueuable;

Its handler contains the application decision, not HTTP or queue plumbing:

public sealed class DepositHandler(IAggregateExecutor aggregates)
    : IRequestHandler<Deposit>
{
    public ValueTask<Result> HandleAsync(
        IRequestContext<Deposit> context,
        CancellationToken ct) =>
        aggregates.ExecuteAsync(
            new Account(context.Request.AccountId),
            account => AggregateOutcome.Commit(account.Deposit(context.Request.Amount)),
            context,
            ct);
}

The application registers the behavior and the durable work caused by its events:

services.AddPortia()
    .AddRequestHandler<DepositHandler>()
    .AddProjector<AccountBalanceProjector>("AccountBalanceProjector", WorkloadScope.PerTenant)
    .AddReactor<DepositReceiptReactor>("DepositReceiptReactor", WorkloadScope.PerTenant)
    .AddFitz(configuration.GetSection("Fitz"));

Per-tenant projectors and reactors declare their source with EventStreamPattern.ForTenant(area, resource); Portia binds the actual tenant realm before reading. Global workloads use an exact EventStreamPattern.ForPattern(realm, area, resource).

An API host explicitly maps HTTP endpoints. A worker host calls the same application setup and adds .AddWorkers(). The handler and domain model do not change when the operation is sent over RPC, placed on a queue, or invoked in-process.

The complete consumer fixture executes one business handler through direct dispatch, HTTP, RPC, and a real Fitz queue, then proves that the resulting events retain actor and causal metadata and drive projectors and reactors.

Persistence without a lowest-common-denominator model

Fitz is Portia's first-party event log, transport, scheduler, and coordination layer. Cassie is the first-party read-model engine for SQL, graph, time-series, and vector workloads.

Neither is hidden behind a generic query language. A projector receives an ordinary application repository built on the backend's native client and schema. That repository implements IProjectionStore and IProjectionBatch only to give Portia the lifecycle and atomic read-model-plus-checkpoint boundary it needs.

PostgreSQL, Snowflake, or another store can therefore be added in userland without changing Portia, writing a custom runner, or translating queries into a framework DSL. If a target cannot atomically commit its changes and checkpoint, it is modeled honestly as an at-least-once reactor effect instead.

Start here

Development

dotnet format Portia.slnx --verify-no-changes
dotnet build Portia.slnx --configuration Release
dotnet test Portia.slnx --configuration Release --no-build --filter "Category!=BrokerIntegration"
docker compose up --detach --wait fitz
dotnet test Portia.slnx --configuration Release --no-build --filter "Category=BrokerIntegration"
docker compose down --volumes

Broker integration uses ws://127.0.0.1:4090/ws by default. Override it with FITZ_TEST_ENDPOINT. CI runs the same format, build, broker-free test, packed-consumer, and broker integration sequence.

License

Portia is licensed under the Apache License, Version 2.0.

About

No description, website, or topics provided.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages