Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScript

Choosing an Approach

Keep dependencies explicit. Axial has one dependency model for v1: workflows read an explicit environment, reusable helpers name service contracts, and layers build the environment at the boundary.

Use this order:

  1. Use records plus Flow.envWith for most application code.
  2. Declare a per-service contract for reusable named services.
  3. Use Layer and Layer.provide to build environments and own resource cleanup.
  4. Use ServiceProvider.get only at .NET host edges where direct IServiceProvider lookup is intentional.

Default Shape

Plain F# records are the default recommendation because they are legible, easy to fake in tests, and easy to refactor.

type ApiDeps = { Orders: IOrderRepo; Email: IEmailSender }

let workflow : Flow<ApiDeps, string, unit> =
    flow {
        let! email = Flow.envWith _.Email
        do! email.SendConfirmation()
    }
Keep the boundary concrete unless a named abstraction clearly pays for itself.

Service Contracts

Reusable helpers can ask for a named service without forcing every application to use the same record shape:

type IHasOrders =
    abstract OrderRepo : IOrderRepo

let save order : Flow<#IHasOrders, OrderError, unit> =
    flow {
        let! orders = Flow.envWith _.OrderRepo
        do! orders.Save order
    }
## Layers

Layers build explicit environments and own cleanup through Scope. Use layer { } when application startup needs to combine several services into one environment.

let appLayer =
    layer {
        let! runtime = BaseRuntime.live
        and! orders = ordersLayer

        return { Runtime = runtime; Orders = orders }
    }
Use layers when construction can fail, when resources need cleanup, or when a host container should be validated once at startup. Plain `let!` is sequential and dependent; sibling `and!` bindings are independent and use `Layer.merge`.

Tutorials

For concrete starting points, use App Record and Layers.

More Detail