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?