F# : Onion architecture in a nutshell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. pure, don't think about IO at all | |
module Domain = | |
let add x y = x + y | |
// 2. think about IO but not its implementation | |
module App = | |
let add (getX: unit -> Async<int32>) y = | |
async { | |
let! x = getX () | |
return Domain.add x y | |
} | |
// 3. IO implementation | |
module Infra = | |
let getX () = async { return 7 } | |
// 4. inject dependencies | |
module Startup = | |
let add = App.add Infra.getX | |
// demo | |
Startup.add 3 | |
|> Async.RunSynchronously | |
|> printfn "%A" // 10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. pure, don't think about IO at all | |
module Domain = | |
let add x y = x + y | |
// 2. think about IO but not its implementation | |
module App = | |
let add (getX: unit -> Async<int32>) y = | |
async { | |
let! x = getX () | |
return Domain.add x y | |
} | |
// 3. IO implementation | |
module Infra = | |
open System.Data.SqlClient | |
let newConnection () = new SqlConnection () | |
let getX conn = async { return 7 } | |
// 4. inject dependencies | |
module Startup = | |
let add y = | |
async { | |
use conn = Infra.newConnection () | |
return! App.add (fun () -> Infra.getX conn) y | |
} | |
// demo | |
Startup.add 3 | |
|> Async.RunSynchronously | |
|> printfn "%A" // 10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1_domain.ts | |
// 1. pure, don't think about IO at all | |
export function add(x: number, y: number) { | |
return x+y; | |
} | |
// 2_app.ts | |
// 2. think about IO but not its implementation | |
import * as Domain from './1_domain' | |
export async function add( getX: () => Promise<number>, y: number) { | |
const x = await getX() | |
return Domain.add(x, y); | |
} | |
// 3_infra.ts | |
// 3. IO implementation | |
import fetch from 'node-fetch'; | |
export async function getX(apiUrl:string): Promise<number> { | |
const response = await fetch(apiUrl, { method: 'GET'}); | |
const data = await response.json(); | |
if (response.ok) { | |
return (data as number); | |
} else { | |
return Promise.reject(`Got an error response ${JSON.stringify(data)}`); | |
} | |
} | |
export function getConfigurationUrl() : string { | |
throw new Error('Function not implemented.'); | |
} | |
// 4_index.ts | |
import * as App from './2_app' | |
import * as Infra from './3_infra' | |
// 4. inject dependencies | |
export function add (y:number){ | |
const apiUrl = Infra.getConfigurationUrl(); | |
return App.add ( ()=> Infra.getX(apiUrl), y); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment