F#: Domain Modeling a Workflow (vol. 4)



Let me first send a Shoutout to the F# Camp for helping me continue to grow as a professional developer down here in Miami, Florida. Their support has assisted my relentless pursuit to achieve mastery.

Scott Wlaschin
Mark Seemann
The InnerLight
Chad Gilbert


I’ve been going HAM in Functional Programming. The following brain dump reflects my current knowledge of Domain Driven Design made Functional.


I define a workflow within Nikeza.Mobile as a series of steps to accomplish an objective.
Thus, the following are two workflows for a Follow and an Unsubscribe command:

module Workflows

open Commands
open IO
open Logic
open Events

type private Workflow = Command -> NotificationEvent list

let handle : Workflow =
    fun command -> command |> function
    | Command.Follow      request -> request
                                      |> tryFollow
                                      |> ResultOf.Follow
                                      |> handle

    | Command.Unsubscribe request -> request
                                      |> tryUnsubscribe
                                      |> ResultOf.Unsubscribe
                                      |> handle


Commands are simply requests that can be executed.
In my case, I have a Follow command and an Unsubscribe command:

module Commands

open Nikeza.Common

type Command =
    | Follow      of FollowRequest
    | Unsubscribe of UnsubscribeRequest


Industry experts within the Software Engineering space recommend that impure functions (i.e. I/O) be done at the edges of a workflow. As a result, I have I/O operations at the front edge of my Logic code.

Here’s some IO requests:

module IO

open Nikeza.Common
open Nikeza.DataTransfer

type TryRequest<'request, 'response, 'errorInfo> =
                'request -> Result<'response, 'errorInfo>

let tryFollow :       TryRequest<FollowRequest, SubscriptionResponse, ProfileId> =
    fun (request:FollowRequest) ->
        Error (request.ProfileId |> ProfileId)

let tryUnsubscribe :  TryRequest<UnsubscribeRequest, SubscriptionResponse, ProfileId> =
    fun (request:UnsubscribeRequest) ->
        Error (request.ProfileId |> ProfileId)

type ResultOf =
    | Follow      of Result<SubscriptionResponse, ProfileId>
    | Unsubscribe of Result<SubscriptionResponse, ProfileId>


The result of executing pure domain logic should be domain events.
Here’s the pure domain logic that handles the result of an IO request:

module Logic

open IO
open Events

type private Logic = ResultOf -> NotificationEvent list

let handle : Logic =
    fun command -> command |> function
    | Follow      response -> response |> function
                                          | Ok    info       -> [SubscriberAdded        info.User]
                                          | Error profileId  -> [SubscriberAddFailed    profileId]

    | Unsubscribe response -> response |> function
                                          | Ok    info       -> [SubscriberRemoved      info.User]
                                          | Error profileId  -> [SubscriberRemoveFailed profileId]


A workflow emits domain events. Hence, looking at the Workflow module, one would observe that the result of the handle function are emitted domain events.

Here are some events:

module Events

open Nikeza.DataTransfer
open Nikeza.Common

type NotificationEvent =
    | LinksDiscovered        of Provider

    | SubscriberAdded        of User
    | SubscriberAddFailed    of ProfileId

    | SubscriberRemoved      of User
    | SubscriberRemoveFailed of ProfileId

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: