Preface
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.
Specifically:
Scott Wlaschin
Mark Seemann
The InnerLight
Chad Gilbert
Intro
I’ve been going HAM in Functional Programming. The following brain dump reflects my current knowledge of Domain Driven Design made Functional.
Workflow
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
Command
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
I/O
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>
Logic
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]
Events
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