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.JavaScriptTutorial: Layers
Layers are for construction time, not business logic time.
Use a layer when you need to:
- build an environment from other services or config
- fail during provisioning before the workflow starts
- own resources that must be cleaned up exactly once
- compose several independent startup steps in parallel
1. The Workflow Still Targets An Environment
open System
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
type IOrders =
abstract Save : string -> Task<unit>
type IClock =
abstract UtcNow : unit -> DateTimeOffset
type AppEnv =
{ Orders: IOrders
Clock: IClock }
let saveOrder (orderId: string) : Flow<AppEnv, string, unit> =
flow {
let! env = Flow.env
do!
ColdTask(fun _ ->
task {
do! env.Orders.Save orderId
return ()
})
let now = env.Clock.UtcNow()
printfn "[%O] saved %s" now orderId
}
SystemThreadingTasksAxialLayers07-layers_02-tutorial.md_page.IOrdersSave: IOrders -> string -> Task<unit>stringAn abbreviation for the CLI type . Basic Types
System.Threading.Tasks.Task`1Represents an asynchronous operation that can return a value. The type of the result produced by this .
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
07-layers_02-tutorial.md_page.IClockUtcNow: IClock -> unit -> DateTimeOffsetSystem.DateTimeOffsetRepresents a point in time, typically expressed as a date and time of day, relative to Coordinated Universal Time (UTC).
07-layers_02-tutorial.md_page.AppEnvOrders: IOrdersClock: IClocksaveOrder: string -> Flow<AppEnv,string,unit>orderId: stringAxial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
flow: FlowBuilderThe universal flow { } computation expression.
env: AppEnvAxial.Flowenv: Flow<'env,'error,'env>Reads the current environment as the successful flow value. Use this when the next step genuinely needs the whole environment value, for example when passing a request context to another helper. For a single dependency or configuration value, prefer Flow.envWith; it keeps the dependency local and makes the workflow easier to scan. A whose successful value is the current environment. let myFlow = Flow.env |> Flow.map (fun env -> env)
ColdTasktask: TaskBuilderBuilds a task using computation expression syntax.
Save: string -> Task<unit>now: DateTimeOffsetUtcNow: unit -> DateTimeOffsetprintfn: Printf.TextWriterFormat<'T> -> 'TPrint to stdout using the given format, and add a newline. The formatter. The formatted result. See Printf.printfn (link: ) for examples.
2. Build Small Layers
let ordersLayer : Layer<unit, string, IOrders> =
Layer.succeed
{ new IOrders with
member _.Save orderId =
task {
// Imagine the real dependency here: open connection, transaction, etc.
printfn "persisting %s" orderId
} }
let clockLayer : Layer<unit, string, IClock> =
Layer.succeed
{ new IClock with
member _.UtcNow() = DateTimeOffset.UtcNow }
ordersLayer: Layer<unit,string,IOrders>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
07-layers_02-tutorial.md_page.IOrdersAxial.Layers.LayerModulesucceed: 'output -> Layer<'input,'error,'output>Creates a layer that succeeds with a fixed output value.
_: IOrdersSave: string -> Task<unit>orderId: stringtask: TaskBuilderBuilds a task using computation expression syntax.
printfn: Printf.TextWriterFormat<'T> -> 'TPrint to stdout using the given format, and add a newline. The formatter. The formatted result. See Printf.printfn (link: ) for examples.
clockLayer: Layer<unit,string,IClock>07-layers_02-tutorial.md_page.IClock_: IClockUtcNow: unit -> DateTimeOffsetSystem.DateTimeOffsetRepresents a point in time, typically expressed as a date and time of day, relative to Coordinated Universal Time (UTC).
UtcNow: DateTimeOffsetGets a object whose date and time are set to the current Coordinated Universal Time (UTC) date and time and whose offset is . An object whose date and time is the current Coordinated Universal Time (UTC) and whose offset is .
let appLayer : Layer<unit, string, AppEnv> =
layer {
let! orders = ordersLayer
and! clock = clockLayer
return
{ Orders = orders
Clock = clock }
}
appLayer: Layer<unit,string,AppEnv>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
07-layers_02-tutorial.md_page.AppEnvlayer: LayerBuilderThe layer { } computation expression for provisioning explicit service environments.
orders: IOrdersordersLayer: Layer<unit,string,IOrders>clock: IClockclockLayer: Layer<unit,string,IClock>Orders: IOrdersClock: IClock4. Provision Failure Happens Before Business Logic
let failingOrdersLayer : Layer<unit, string, IOrders> =
Layer.fromTask (fun _ _ ->
task {
return Exit.Failure (Cause.Fail "database connection string missing")
})
failingOrdersLayer: Layer<unit,string,IOrders>Axial.Layers.Layer`3Represents a provisioning step that builds an explicit environment inside a scope. The input environment required to build the layer. The typed failure produced during provisioning. The environment or service bundle produced by the layer.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
07-layers_02-tutorial.md_page.IOrdersAxial.Layers.LayerModulefromTask: ('input * Scope -> CancellationToken -> Task<Exit<'output,'error>>) -> Layer<'input,'error,'output>Creates a layer from a raw task provisioning function. .NET only
task: TaskBuilderBuilds a task using computation expression syntax.
Axial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
FailureThe workflow failed due to a specific cause.
Axial.Cause`1Represents the cause of a failed workflow. The type of the domain-specific failure value.
FailAn expected domain-specific failure.
5. Resource Ownership
type FakeConnection() =
interface IAsyncDisposable with
member _.DisposeAsync() =
ValueTask(Task.CompletedTask)
let connectionLayer : Layer<unit, string, FakeConnection> =
Layer.acquireRelease
(Layer.succeed (new FakeConnection()))
(fun connection _ct -> connection.DisposeAsync().AsTask())6. Run Through Layer.provide
let run () = task {
let! exit =
saveOrder "A-100"
|> Layer.provide appLayer
|> Flow.startTask ()
match exit with
| Exit.Success () -> printfn "done"
| Exit.Failure cause -> printfn "failed: %A" cause
}
run: unit -> Task<unit>task: TaskBuilderBuilds a task using computation expression syntax.
exit: Exit<unit,string>saveOrder: string -> Flow<AppEnv,string,unit>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.Layers.LayerModuleprovide: Layer<'input,'error,'environment> -> Flow<'environment,'error,'value> -> Flow<'input,'error,'value>Builds an environment with a layer, runs a downstream flow, and always closes the layer scope. This is the provisioning boundary. It creates a fresh scope, builds the supplied layer inside that scope, runs the downstream flow with the built environment, and finalizes all acquired resources when the downstream flow completes or fails. The layer that builds the downstream environment. The flow to run with the provided environment. A flow that requires only the input environment of the layer. let program () = let runtimeLayer = Layer.succeed "production" let workflow = Flow.envWith String.length Layer.provide runtimeLayer workflow
appLayer: Layer<unit,string,AppEnv>Axial.FlowstartTask: 'env -> Flow<'env,'error,'value> -> Task<Exit<'value,'error>>Starts the workflow immediately and returns a task handle for its final exit. The work is already in flight when this returns. Use Flow.toAsync for a cold handle. The environment used by the workflow. The workflow to start. A task that completes with the workflow exit. let running = workflow |> Flow.startTask environment
Axial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
SuccessThe workflow completed successfully.
printfn: Printf.TextWriterFormat<'T> -> 'TPrint to stdout using the given format, and add a newline. The formatter. The formatted result. See Printf.printfn (link: ) for examples.
FailureThe workflow failed due to a specific cause.
cause: Cause<string>- construct or choose the layer
- provide it once
- run the workflow
That is much cleaner than manually opening and closing startup resources around every feature entry point.

