Exploring F# through modeling #4

Introduction

In three earlier posts we have explored modeling a Rock-paper-scissor game in F#. We have modeled the domain and infrastructure. In the last post we looked at refactoring and C# interop. In this post we’ll go back to basics when we explore using C# infrastructure in our solution. We’re going to look at record types, classes and interface implementation.

Scenario

We want to use our Domain with Commands and Events together with some C# infrastructure. The infrastructure might have interfaces that our F# solution need to implement to use the infrastructure. We could do some adapter clue, but in this post we’re going to implement the interfaces in F#.

Command interface

The C# interface that the infrastructure uses looks like this;

public interface ICommand
{
    Guid AggregateId { get; }
    Guid CorrelationId { get; }
}

So let’s take a look at one of our commmands – the CreateGameCommand.

type CreateGameCommand = {
          playerName: string
          firstMove: Move
          name:string
          id:string } 

We have used interfaces before in our solution – the Event marker interface.

type Event = 
    interface
    end

type MoveMadeEvent=
    {
    playerName:string
    move:Move
    } 
    interface Event

To implement the C# interface we use the same syntax, specifying the the interface then each member of the interface as follow;

type CreateGameCommand=
    {playerName: string
     firstMove: Move
     name:string
     id:System.Guid} interface Commanding.ICommand with
                        member x.AggregateId = x.id
                        member x.CorrelationId = Guid.NewGuid()

Note that all interface implementations in F# are Explicit

Here we changed the id to a Guid to align with the interface, pointing out the id property. The Correlation ID don’t have a corresponding property yet, as we create a new guid. The Correlation ID would return a new Guid each time – not ok. The correlation ID is just an identifier of each instance, nothing we need to set our self when creating a command. So what are our options regarding the Correlation ID ? If we want to stick to a record type we need to add a property to match Correlation ID.

  type CreateGameCommand =
    {playerName: string
     firstMove: Move
     name:string
     id:System.Guid
     correlationId:System.Guid} 
     interface Commanding.ICommand with
                        member x.AggregateId = x.id
                        member x.CorrelationId = x.correlationId

If we would like to skip passing a correlation ID, we could create an factory, or we need to create a class instead. A simple factory;

let createGameCommand playerName firstMove name gameId = 
{ playerName = playerName; firstMove = firstMove; name = name; id = gameId; correlationId = Guid.NewGuid() }

In this case a record type fits better (Type inference, immutability and pattern matching – Record types vs Classes).

Just to have a look – lets go for the class approach;

type CreateGameCommand(playerName:string, firstMove:Move, name:string, gameId:Guid, correlationId:Guid) =
     member x.playerName = playerName
     member x.firstMove = firstMove
     member x.name = name
     member x.id = gameId
     member x.correlationId = correlationId

Adding the interface to the class;

type CreateGameCommand(playerName:string, firstMove:Move, name:string, gameId:Guid, correlationId:Guid) =
    interface Commanding.ICommand with 
        member x.AggregateId = x.id
        member x.CorrelationId = x.correlationId
    member x.playerName = playerName
    member x.firstMove = firstMove
    member x.name = name
    member x.id = gameId
    member x.correlationId = correlationId

Event interface

The C# interface that the infrastructure uses for Event looks like this;

 public interface IEvent
 {
    Guid CorrelationId { get; set; }
    Guid SourceId { get; }
 }

The SourceId would be the aggregate related to the change. And Correlation ID what command resulted in this event. This introduces a new challenge. The infrastructure needs to set something in our immutable world. Our events are record types – immutable. But to comply with this interface we could declare one of it’s properties as mutable;

type MoveMadeEvent=
    {
    playerName:string
    move:Move
    id:Guid
    mutable correlationId:Guid
    } 
    interface Events.IEvent with
        member x.SourceId = x.id
        member x.CorrelationId with get() = x.correlationId
        member x.CorrelationId with set(v) = x.correlationId <- v

We could skip the get();

member x.CorrelationId = x.correlationId
member x.CorrelationId with set(v) = x.correlationId <- v

Conclusion

Hope the infrastructure is worth it. Our record types is now bulkier. There might have been other way to use the infrastructure, wrapping it's contracts. But we got a good back to basics look on record types, classes and interface implementation in F#. Due to that the infrastructure where in C#, we haven't looked in creating interfaces in F#. Below is some resources on the topic we touched upon. Hope to get back to modeling in future posts.

Enjoy!

Resources

Leave a Reply

Close Menu