F# Onion Architecture in 92 lines of code

jkone27
3 min readMar 25, 2022

Understanding application dependencies in a single script file

Red Onions

Setup (skip if you are ready)

brew install --cask dotnet-sdk //macwinget install -e --id Microsoft.dotnet //win

test your setup with

dotnet fsi
>
"hello";; //always append commands with ;; else is multiline
#quit;; //interactive commands have a trailing # symbol

install vscode and ionide extension (or if you have rider or visualstudio also good to go already),

open an empty folder and create a script file

mkdir onion && cd onion && touch onion.fsx

Here is the code

What is Onion Architecture?

Thanks to the source of this pic, here the full article: https://jkphl.is/articles/clear-architecture-php/

I will simplify this example design by Merging some of the Ports parts within Application..

Domain

First things first we add our domain, we have an Order, and we want to model for now a change price functionality to keep it as simple as possible.

domain

Infrastructure

We want to define our infrastructure layer and ideally we would like also api client to map to domain types, but i felt like in real world many times we allow for a bit more slippery interactions here, letting the application service layer interact directly with api client dtos*.

infra

(* this is not the 100% best practice) You can adjust the example if you wanted to also map apiclient to it’s domain types, but I found this many times in practice it’s a bit irrelevant and adds more clutter/indirection to your code**. (**works-on-my-onion)

Services

This is where our main usecases / workflows / application services sit, in this layer we will map our “end to end” business logic.

Services

We should strive to only compose here, and keep most of our real business logic within Domain (that’s the part we would unit test mostly generally).

Application/EntryPoint

Application EntryPoint

This is where our apps runs and it’s usually the entry point of our execution within a process on the OS/container. Also part of Application ideally*, but it might have a bit of extra dependencies like configuration, deployment, parameters, env vars, etc…).

In case of a web app, this is where our controllers (mvc style) / http-handlers (functional style) are. As you know HTTP sits at the Application Level of the Internet stack.

In this layer we also register our dependencies with DI, which in functional languages with default curried functions, is just partial function application by default, so we don’t need interfaces to be SOLID and apply IOC, just use regular function composition*.

*I could have used also interfaces and e.g. microsoft DI framework for IOC… or others more OO compatible DI frameworks, this is just for simplicity.

Testability

Mock module for testing

We can easilly mock our dependencies, as they are functions, but with a DI framework would work seamlessly registering the mocks.

And write our test suite…

Tests

FIN

Hope you have enjoyed this and feel free to comment if you have different opinions on the layered onion/exagonal architecture pattern, Cheers!

--

--