Giter Club home page Giter Club logo

domainmodelingmadefunctional's Introduction

This directory contains code samples related to the book "Domain Modeling Made Functional"

IMPORTANT: This code is not actively maintained. Feel free create issues and PRs but I might not look at them for a long time :)

Organization

  • /src/OrderTaking contains the workflow as part of a complete bounded context.
  • /src/OrderTakingEvolved contains the workflow after the changes in the Evolution chapter.

Getting started

Ensure that you have .NET Core installed. Download link.

After downloading this code, do the following steps:

  • Run dotnet build to download all the NuGet packages and compile the project
  • If you have problems, please create an issue on this repo

domainmodelingmadefunctional's People

Contributors

akoslukacs avatar horacegonzalez avatar swlaschin avatar yuriyostapenko avatar yuuuxt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

domainmodelingmadefunctional's Issues

Domain Modelling Made Functional Book

Hi,

Domain Modelling Made Functional Book

Just bought the book from amazon.co.uk. Could you please post the diagram that is meant to be on the top of page 18! The diagram that is in my copy ‘Book version: P1.0-January 2018’ has the same diagram on page 18 as is on page 17 i.e. the ‘Problem space (real world) - Solution space (domain world)’. I have looked around and can find no mention of this error or the correct diagram.

Best regards

Stephen

asyncResult not working

Hello Scott!
I have been stuck with the "validateOrder function" in the "PlaceOrder.Implementation.fs" file for a while and I hope to get some help from you.
I tried to post my issue in prag prog "The website where I got your book from", but it seems like some legislation in the USA has caused them to make the forum unavaillable.

Bellow is the piece of code that seems not to be working:

let validateOrder : ValidateOrder =

fun checkProductCodeExists checkAddressExists unvalidatedOrder ->


    printfn "IN validate order unvalidatedOrder = %A" unvalidatedOrder

    printfn "IN validate order unvalidatedOrder.OrderId = %A" unvalidatedOrder.OrderId


    asyncResult {


        //printfn "IN validate order customerInfo = %A" customerInfo

        let! orderId = 
            unvalidatedOrder.OrderId 
            |> toOrderId
            |> AsyncResult.ofResult

        printfn "orderId = %A" orderId

        let! customerInfo = 
            let unvalidateCustomerInfo = unvalidatedOrder.CustomerInfo 
            unvalidateCustomerInfo
            |> toCustomerInfo
            |> AsyncResult.ofResult

        let! checkedShippingAddress = 
            let unvalidateShippingAddress = unvalidatedOrder.ShippingAddress 
            unvalidatedOrder.ShippingAddress 
            |> toCheckedAddress checkAddressExists

        let! shippingAddress = 
            checkedShippingAddress 
            |> toAddress 
            |> AsyncResult.ofResult
        let! checkedBillingAddress = 
            unvalidatedOrder.BillingAddress 
            |> toCheckedAddress checkAddressExists
        let! billingAddress  = 
            checkedBillingAddress
            |> toAddress 
            |> AsyncResult.ofResult
        let! lines = 
            unvalidatedOrder.Lines 
            |> List.map (toValidatedOrderLine checkProductCodeExists) 
            |> Result.sequence // convert list of Results to a single Result
            |> AsyncResult.ofResult

        let validatedOrder : ValidatedOrder = {
            OrderId  = orderId 
            CustomerInfo = customerInfo 
            ShippingAddress = shippingAddress 
            BillingAddress = billingAddress  
            Lines = lines 
        }




        return validatedOrder 
    }

This instruction

let! orderId =
unvalidatedOrder.OrderId
|> toOrderId
|> AsyncResult.ofResult

runs the unvalidatedOrder.OrderId |> toOrderId function with no problem
then it gets stuck at |> AsyncResult.ofResult.

I would appreciate if you could help me figure out what the issue is with
AsyncResult.ofResult as I feel like it is there that everything is stuck.

fail to build on ubuntu

cpchung:DomainModelingMadeFunctional$ ./build.sh 
Paket version 5.238.1
The last restore is still up to date. Nothing left to do.
Performance:
 - Runtime: 81 milliseconds
Building project with version: LocalBuild
Shortened DependencyGraph for Target Build:
<== Build
   <== Restore
      <== InstallDotNetCLI
         <== Clean

The running order is:
  - Clean
  - InstallDotNetCLI
  - Restore
  - Build
Running build with 1 worker
Starting Target: Clean 
Deleting contents of ./build/
Finished Target: Clean
Starting Target: InstallDotNetCLI (==> Clean)
dotnet --info
/home/cpchung/.local/share/dotnetcore/dotnet --info
cmd 2.1.4 already installed in LocalApplicationData
Finished Target: InstallDotNetCLI
Starting Target: Restore (==> InstallDotNetCLI)
/home/cpchung/.local/share/dotnetcore/dotnet restore
MSBUILD : error MSB1025: An internal failure occurred while running MSBuild.
System.InvalidOperationException: The terminfo database is invalid.
   at System.TermInfo.Database..ctor(String term, Byte[] data)
   at System.TermInfo.Database.ReadDatabase(String term, String directoryPath)
   at System.TermInfo.Database.ReadDatabase(String term)
   at System.TermInfo.Database.ReadActiveDatabase()
   at System.ConsolePal.TerminalFormatStrings.<>c.<.cctor>b__27_0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at System.ConsolePal.EnsureInitializedCore()
   at System.ConsolePal.ControlCHandlerRegistrar.Register()
   at System.Console.add_CancelKeyPress(ConsoleCancelEventHandler value)
   at Microsoft.Build.CommandLine.MSBuildApp.Execute(String[] commandLine) in E:\A\_work\17\s\src\MSBuild\XMake.cs:line 526

Unhandled Exception: System.InvalidOperationException: The terminfo database is invalid.
   at System.TermInfo.Database..ctor(String term, Byte[] data)
   at System.TermInfo.Database.ReadDatabase(String term, String directoryPath)
   at System.TermInfo.Database.ReadDatabase(String term)
   at System.TermInfo.Database.ReadActiveDatabase()
   at System.ConsolePal.TerminalFormatStrings.<>c.<.cctor>b__27_0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at System.ConsolePal.EnsureInitializedCore()
   at System.ConsolePal.ControlCHandlerRegistrar.Register()
   at System.Console.add_CancelKeyPress(ConsoleCancelEventHandler value)
   at Microsoft.Build.CommandLine.MSBuildApp.Execute(String[] commandLine) in E:\A\_work\17\s\src\MSBuild\XMake.cs:line 748
   at Microsoft.Build.CommandLine.MSBuildApp.Main(String[] args) in E:\A\_work\17\s\src\MSBuild\XMake.cs:line 215
Running build failed.
Error:
System.Exception: dotnet restore failed
  at Microsoft.FSharp.Core.PrintfModule+PrintFormatToStringThenFail@1379[TResult].Invoke (System.String message) [0x00001] in <5893d081904cf4daa745038381d09358>:0 
  at FSI_0005.Build.runDotnet (System.String workingDir, System.String args) [0x00043] in <4a3ea9301b374a8aa6ab1c1f125b528d>:0 
  at [email protected] (System.String p) [0x0000a] in <4a3ea9301b374a8aa6ab1c1f125b528d>:0 
  at Microsoft.FSharp.Collections.SeqModule.Iterate[T] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] action, System.Collections.Generic.IEnumerable`1[T] source) [0x0002d] in <5893d081904cf4daa745038381d09358>:0 
  at [email protected] (Microsoft.FSharp.Core.Unit _arg3) [0x0000b] in <4a3ea9301b374a8aa6ab1c1f125b528d>:0 
  at Fake.TargetHelper.runSingleTarget (Fake.TargetHelper+TargetTemplate`1[a] target) [0x00049] in <5aa25359ccf1c534a74503835953a25a>:0 

---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target             Duration
------             --------
Clean              00:00:00.0014879
InstallDotNetCLI   00:00:00.1250078
Restore            Failure
Total:             00:00:00.5667757
---------------------------------------------------------------------
Status:            Failure
---------------------------------------------------------------------
---------------------------------------------------------------------
  1) System.Exception: dotnet restore failed
  at Microsoft.FSharp.Core.PrintfModule+PrintFormatToStringThenFail@1379[TResult].Invoke (System.String message) [0x00001] in <5893d081904cf4daa745038381d09358>:0 
  at FSI_0005.Build.runDotnet (System.String workingDir, System.String args) [0x00043] in <4a3ea9301b374a8aa6ab1c1f125b528d>:0 
  at [email protected] (System.String p) [0x0000a] in <4a3ea9301b374a8aa6ab1c1f125b528d>:0 
  at Microsoft.FSharp.Collections.SeqModule.Iterate[T] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] action, System.Collections.Generic.IEnumerable`1[T] source) [0x0002d] in <5893d081904cf4daa745038381d09358>:0 
  at [email protected] (Microsoft.FSharp.Core.Unit _arg3) [0x0000b] in <4a3ea9301b374a8aa6ab1c1f125b528d>:0 
  at Fake.TargetHelper.runSingleTarget (Fake.TargetHelper+TargetTemplate`1[a] target) [0x00049] in <5aa25359ccf1c534a74503835953a25a>:0 


cpchung:DomainModelingMadeFunctional$ dotnet --info
A compatible installed .NET Core SDK for global.json version [2.1.4] from [/home/cpchung/code/DomainModelingMadeFunctional/global.json] was not found
Install the [2.1.4] .NET Core SDK or update [/home/cpchung/code/DomainModelingMadeFunctional/global.json] with an installed .NET Core SDK:
  3.0.101 [/usr/share/dotnet/sdk]

Host (useful for support):
  Version: 3.0.1
  Commit:  19942e7199

.NET Core SDKs installed:
  3.0.101 [/usr/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.App 3.0.1 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.0.1 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download

Use binding in an asyncResult computation expression

First off, thanks for the great book and this repo!

I think I'm running into a bug where use-bound values in asyncResult computation expressions are disposed prematurely. Consider this snippet:

let a = async {
    use f = { new IDisposable with member x.Dispose() = printfn "Disposed" }

    do! Async.Sleep 50
    printfn "async with do! ending"
    return Ok () } |> Async.RunSynchronously

printfn ""
let b = asyncResult {
    use f = { new IDisposable with member x.Dispose() = printfn "Disposed" }

    printfn "asyncResult without do! ending" } |> Async.RunSynchronously

printfn ""
let c = asyncResult {
    use f = { new IDisposable with member x.Dispose() = printfn "Disposed" }

    do! AsyncResult.sleep 10
    printfn "asyncResult with do! ending" } |> Async.RunSynchronously

Running this prints:

async with do! ending
Disposed

asyncResult without do! ending
Disposed

Disposed
asyncResult with do! ending

Do you have idea why f in the last case gets disposed early? Cheers.

How to interact with presistent?

DDD told us db is not important, it's none of the domain's bussiness, it's infrastructure side. That's fine and reasonable. But most ddd demos just ignore the implementation of presistent and which confused me most. How to design my table besides domain and in which layer my dbcontext works.
So the problem come to I learned DDD but I don't know how to save my data.

Fails to build because module OrderTaking.PlaceOrder.Implementation is internal

Maybe I'm missing something dumb, but if I grab the code as-is, I can't build using latest VS2019 or VS2019 preview:

Severity	Code	Description	Project	File	Line	Suppression State
Error	FS0410	The type 'CheckedAddress' is less accessible than the value, member or type 'val checkAddressExists : unvalidatedAddress:UnvalidatedAddress -> AsyncResult<Implementation.CheckedAddress,Implementation.AddressValidationError>' it is used in.	OrderTaking	C:\Users\Kent\Repository\DomainModelingMadeFunctional\src\OrderTaking\PlaceOrder.Api.fs	46	Active
Error	FS0410	The type 'AddressValidationError' is less accessible than the value, member or type 'val checkAddressExists : unvalidatedAddress:UnvalidatedAddress -> AsyncResult<Implementation.CheckedAddress,Implementation.AddressValidationError>' it is used in.	OrderTaking	C:\Users\Kent\Repository\DomainModelingMadeFunctional\src\OrderTaking\PlaceOrder.Api.fs	46	Active
Error	FS0410	The type 'HtmlString' is less accessible than the value, member or type 'val createOrderAcknowledgmentLetter : pricedOrder:PricedOrder -> Implementation.HtmlString' it is used in.	OrderTaking	C:\Users\Kent\Repository\DomainModelingMadeFunctional\src\OrderTaking\PlaceOrder.Api.fs	56	Active
Error	FS0410	The type 'OrderAcknowledgment' is less accessible than the value, member or type 'val sendOrderAcknowledgment : orderAcknowledgement:Implementation.OrderAcknowledgment -> Implementation.SendResult' it is used in.	OrderTaking	C:\Users\Kent\Repository\DomainModelingMadeFunctional\src\OrderTaking\PlaceOrder.Api.fs	61	Active
Error	FS0410	The type 'SendResult' is less accessible than the value, member or type 'val sendOrderAcknowledgment : orderAcknowledgement:Implementation.OrderAcknowledgment -> Implementation.SendResult' it is used in.	OrderTaking	C:\Users\Kent\Repository\DomainModelingMadeFunctional\src\OrderTaking\PlaceOrder.Api.fs	61	Active

This makes sense because functions in the (public) OrderTaking.PlaceOrder.Api module are returning types from the (internal) OrderTaking.PlaceOrder.Implementation module. Was this a recent improvement to the F# compiler to catch this case?

For now, I can simply remove the internal modifier and it builds.

Noticed Differences, so just want to clarify the things

@swlaschin
Hi Scott, thanks for the amazing book. Before I tried F# programming, I read the other popular books of DDD, but I found your book very helpful and easy to understand. Theoretically, the topics make sense and cleared my understanding about the various concepts and methodologies.
I just want to clarify 2 points which I notice in book and sample code.

  1. The customer and order both are different aggregates, and in book you mentioned that the aggregate root of one aggregate should reference the identity of another aggregate root not the entire object. (at location 2418 in Kindle desktop version, or in chapter 5 – Aggregates topic.)
    Below is the statement from the book:

"the Customer and the Order are distinct and independent aggregates. They each are responsible for their own internal consistency, and the only connection between them is via the identifiers of their root objects."

But in code sample, Order referencing the CustomerInfo instead of CustomerId.

  1. Another point is, in chapter-3, the topic is “Avoid Domain Events Within a Bounded Context" inside the section Workflow within bounded context, (location is 1071 in kindle desktop), you mentioned that the

In an object-oriented design, it is common to have Domain Events raised internally within a bounded context. In that approach, a workflow object raises an OrderPlaced event. Next a handler listens for that event and sends the order acknowledgment, then another handler generates a BillableOrderPlaced event, and so on.

In a functional design, we prefer not to use this approach because it creates hidden dependencies. Instead, if we need a “listener” for an event, we just append it to the end of workflow

But, in chapter-6, the topic is "Consistency Between Aggregates in the Same Context" in Consistency (location is 3022 in kindle desktop),

In general, a useful guideline is “only update one aggregate per transaction.”

If more than one aggregate is involved, we should use messages and eventual consistency as described above, even though both aggregates are within the same bounded context.

I know you have mentioned that, it depends on and sometimes consider the updates in the same transaction if the considered by the business workflow.

I just want to clarify the points as I am learning the DDD and F# and want to practice the design and development with those approaches.

Initially I noticed these 2, but then I observe third one.
I know you haven't mentioned that the code has implemented using Onion/Clean Architecture based, but there is one topic regarding the architecture in end of chapter-3.
So according to the Onion architecture, All dependencies must point inward. But in repo, the Internal Types are referring to the Public Types which means the dependency points outward, as the public types consider as command type or request type while the internal types are the domain types.

Thanks again for the excellent book.

Porting to .NET Core/Standard

Hi, Scott!

Would you be interested in porting this code to .net core/standard? I've received some questions on the F# slack group about build errors running this on mac/linux w/t ionide. I noticed the projects are targeting net461 and thought this may be responsible for some of the grief.

I'd be willing to help with this.

How to read CheckedAddress from a data store

Hello @swlaschin ! I know you are not maintaining this repo but I will try this anyways, who knows right? I have an implementation question that I have not been able to find the answer to. I'm currently trying to implement some of the ideas from the book in a Kotlin project, so there may be some language barriers.

But, in the book there is a service (function) that creates a CheckedAddress type. This type is private to the service and may only be created there so as to enforce invariants. I like this pattern a lot. But, when the construction of the type is so limited, how would you approach creating a CheckedAddress type from a data store? It would be nice to not have to open a back door in the domain, but that is what I have resorted to. Some languages and frameworks may offer instantiation of such types through reflection, but I haven't seen any such feature readily available in the Kotlin/Spring JPA world. I'm curious though what your thoughts on this subject would be.

Thank you for a great book and possibly for your answer to this long shot question.

//Erik

Question: Is there a complete implementation including persistence and serialization?

I've been faithfully following along with the book's code examples and referring to the bundled code as well as this repo... I was disappointed to reach the chapter on persistence only to find that the persistence implementation was omitted from the final implementation of the application.

Is the full implementation available anywhere, or are there any plans to produce it?
I'd really like to see how the serialization and persistence aspects should be organized in a fully working solution.

Thanks in advance!

AsyncResult.catch don't work as expected

Hello first, a really good book 👍 congrats.
Here my doubt.
I try to capture an exception in asyncResult with AsyncResult.catch but It doesn't work as expected. Here my tests:

Async example:

[<Fact>]
let asyncExceptionTest() = 
  let r = async {
              failwith "Ohh nooo!!!"
              return "ok"
            }
          |> Async.Catch
          |> Async.RunSynchronously
  match r with 
  | Choice1Of2 a -> printfn "ok --> %A" a        
  | Choice2Of2 e -> printfn "error -->%A" e

This example works well. It captures the exception. Now the same example with asyncResult.

[<Fact>]
let asyncResultTest() = 
  let r = asyncResult {
              failwith "Ohh nooo!!!"
              return "ok"
            }
          |> AsyncResult.catch ( fun ex -> printfn "%A" ex; ex)
          |> Async.RunSynchronously
  match r with 
  | Ok a -> printfn "ok --> %A" a        
  | Result.Error e -> printfn "error -->%A" e    

Here the code catch didn't catch the exception.
Any ideas why?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.