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.
We want to use our Domain with Commans and Events together with some C# infrastructure. The infrastruct might have interfaces that our F# solution need to implement to use the infrastrcutre. We could do some adapter clue, but in this post we're going to implement the interfaces in F#.
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 memerber 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 CorreclationId don't have a corresponding property yet, as we create a new guid. The CorrelationId would return a new Guid each time - not ok. The correlationId 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 CorrelationId ? If we want to stick to a record type we need to add a property to match CorrelationId.
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 correlcationId, 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, Immubility 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
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 CorrelationId what command resulted in this event. This introduces a new challange. 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 infrastrcutrue, 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 furute posts.
Enjoy!
"Service Bus" -> "ServiceBus"
"handling command for a given" -> "handling commands"
"the node would go done" go?