Project Structure

June 1, 2026 · View on GitHub

This guide explains how Abies projects are organized, covering both browser (WASM) and server deployments.

Browser (WASM) Project

A minimal browser project:

MyApp.Wasm/
├── MyApp.Wasm.csproj       ← BlazorWebAssembly SDK, Picea.Abies.Browser ref
├── Program.cs              ← Entry point: Runtime.Run<App, Model, Unit>()
└── wwwroot/
    ├── index.html           ← HTML shell with loading placeholder
    └── css/
        └── app.css          ← Application styles

Key Files

Program.cs — The entry point. A single line starts the runtime:

await Picea.Abies.Browser.Runtime.Run<App, Model, Unit>();

Or with an interpreter for side effects:

await Picea.Abies.Browser.Runtime.Run<App, Model, Unit>(
    interpreter: MyInterpreter.Handle);

wwwroot/index.html — The HTML shell. Include a loading placeholder for fast first paint:

<body>
    <div id="main">Loading...</div>
</body>

.csproj — Uses the BlazorWebAssembly SDK for WASM compilation:

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Picea.Abies.Browser" Version="2.1.*" />
  </ItemGroup>
</Project>

Server Project

A minimal server project:

MyApp.Server/
├── MyApp.Server.csproj     ← Web SDK, Picea.Abies.Server.Kestrel ref
├── Program.cs              ← Entry point: MapAbies<App, Model, Unit>(...)
└── Properties/
    └── launchSettings.json  ← Development URLs and profiles

Key Files

Program.cs — Configures Kestrel and maps the Abies endpoint:

using Picea.Abies.Server.Kestrel;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapAbies<App, Model, Unit>("/",
  mode: new RenderMode.InteractiveServer("/ws"),
    interpreter: MyInterpreter.Handle);

app.Run();

.csproj — Uses the standard Web SDK:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Picea.Abies.Server.Kestrel" Version="2.1.*" />
  </ItemGroup>
</Project>

Shared Application Logic

For real applications, extract the application logic into a shared library:

MyApp/
├── MyApp.csproj             ← Class library with app logic
├── App.cs                   ← Program<Model, Unit> implementation
├── Model.cs                 ← Application state
├── Messages.cs              ← Message types
└── Views/
    ├── HomePage.cs           ← View functions for home page
    ├── ArticlePage.cs        ← View functions for article page
    └── Components.cs         ← Shared view functions

MyApp.Wasm/
├── MyApp.Wasm.csproj        ← References MyApp
└── Program.cs               ← Runtime.Run<App, Model, Unit>()

MyApp.Server/
├── MyApp.Server.csproj      ← References MyApp
└── Program.cs               ← MapAbies<App, Model, Unit>(...)

This is the recommended structure for any non-trivial application. The shared library contains:

  • All application logic — Model, Messages, Transition, View, Subscriptions
  • No platform dependencies — References only Picea.Abies (the core package)

The platform projects (Wasm and Server) are thin shells that only wire up the runtime.

Real-World Example: Conduit

The Conduit application is a full-featured social blogging platform (Medium clone) built with Abies. Here's its structure:

For current local run commands and testing entry points, see the dedicated Conduit README.

Picea.Abies.Conduit/                    ← Shared application logic
├── Domain/                              ← Domain model
│   ├── Article.cs
│   ├── Comment.cs
│   ├── Profile.cs
│   └── User.cs
├── ReadModel/                           ← Query-side models
│   ├── ArticleFeed.cs
│   └── TagList.cs
└── ...other application concerns

Picea.Abies.Conduit.App/                ← MVU application
├── App.cs                               ← Program<Model, Unit>
├── Model.cs                             ← Application state
├── Messages.cs                          ← Message types
├── Routing.cs                           ← URL → Page routing
└── Views/                               ← View functions

Picea.Abies.Conduit.Api/                ← REST API backend
├── Endpoints/                           ← Minimal API endpoints
├── Authentication/                      ← JWT auth
└── Infrastructure/                      ← Database, DI

Picea.Abies.Conduit.Wasm.Host/          ← Browser host
└── Program.cs                           ← One-liner entry point

Picea.Abies.Conduit.Server/             ← Server host
└── Program.cs                           ← MapAbies + API endpoints

Picea.Abies.Conduit.Tests/              ← Unit tests
Picea.Abies.Conduit.Wasm.Tests/         ← WASM-specific tests
Picea.Abies.Conduit.Api.Tests/          ← API integration tests
Picea.Abies.Conduit.Testing.E2E/        ← Playwright E2E tests

Testing Structure

Abies projects follow a predictable test structure:

MyApp.Tests/              ← Unit tests for pure functions
├── TransitionTests.cs     ← Test Transition(model, message) => (model, command)
├── ViewTests.cs           ← Test View(model) => Document
└── RoutingTests.cs        ← Test URL parsing and route matching

MyApp.Testing.E2E/        ← End-to-end tests (Playwright)
├── CounterTests.cs        ← Full browser interaction tests
└── NavigationTests.cs     ← Client-side routing tests

Because Transition and View are pure functions, unit testing is trivial:

[Test]
public async Task Increment_IncreasesCount()
{
    var model = new Model(Count: 5);
    var (result, command) = Counter.Transition(model, new CounterMessage.Increment());
    await Assert.That(result.Count).IsEqualTo(6);
    await Assert.That(command).IsTypeOf<Command.None>();
}

Naming Conventions

ConventionExample
App libraryMyApp
Browser hostMyApp.Wasm
Server hostMyApp.Server
Unit testsMyApp.Tests
E2E testsMyApp.Testing.E2E
API backendMyApp.Api
API testsMyApp.Api.Tests

For the Picea ecosystem, prefix with Picea.Abies.:

ConventionExample
App libraryPicea.Abies.Conduit
Browser hostPicea.Abies.Conduit.Wasm.Host
Server hostPicea.Abies.Conduit.Server

See Also