Handle state with functions the FP/F# way

Viewed 176

Here is some interface IOrderSender which defines a business operation (order sending) with some implementation KafkaOrderSender (via Kafka producer in this example).

The code was rewritten from C# with preserving the original OOP-style:

type CustomerId = string

type IOrderSender =
    abstract SendOrders: CustomerId -> Order list -> unit

type KafkaOrderSender(config: IConfiguration) =
    let streamConfig = config.GetSection("OrderStream")

    let producerConfig = new ProducerConfig(BootstrapServers = streamConfig.GetValue("BootstrapServers"))
    let producer = new Producer<string, string>(producerConfig)

    let ordersTopic: string = streamConfig.GetValue("Topic")

    interface IOrderSender with
        member this.SendOrders customerId orders =
            for order in orders do
                let orderJson = JsonConvert.SerializeObject<Order>(order)
                let msg = new Message<string, string>(Key = customerId, Value = orderJson)
                do producer.BeginProduce(ordersTopic, msg)

    interface IDisposable with
        member this.Dispose() =
            do producer.Flush(CancellationToken.None)
            do producer.Dispose()

Here we have:

  • interface (IOrderSender)

  • implementation (KafkaOrderSender)

  • one external dependency (IConfiguration)

  • internal state (producer, needs to be shared between calls)

  • the state needs to be closed/disposed (IDisposable)

This interface and implementation registered in some DI container (e.g. ASP.NET Core with Giraffe):

let configureServices (services : IServiceCollection) =
    services.AddGiraffe() |> ignore
    services.AddSingleton<IOrderSender, KafkaOrderSender>() |> ignore

This object then resolved via DI and used in application logic:

let handleOrders (customerId: CustomerId) (orders: Orders): HttpHandler =
    fun (next : HttpFunc) (ctx : HttpContext) ->
        // Resolve
        let orderSender = ctx.GetService<IOrderSender>()
        // Use
        do orderSender.SendOrders customerId orders
        return! text "OK" next ctx

Is there a better way to I implement the IOrderSender, in a more idiomatic functional way (module instead of class, partial application instead of dependencies via constructor)?

How to handle shared state (producer) and how to implement disposal of the shared state in this case?

0 Answers
Related