Introduction

In our last two posts, we compared F# and C# side-by-side in both theory and implementation. Today, let’s explore a practical use case that’s increasingly common in modern systems: using F# for business logic and C# for infrastructure, especially in a microservice environment.

This hybrid approach allows teams to get the best of both worlds:

  • F#’s clarity and correctness for domain modeling and core computation
  • C#’s ecosystem maturity and tooling for web APIs, DI, and integration

Let’s break it down with examples, structure, and a real-world integration plan.


📐 Why Mix F# and C#?

Here’s a typical scenario:

LayerF# BenefitsC# Benefits
Domain LogicDiscriminated unions, immutability, safetyVerbose, but doable
API LayerCan work, but tooling weakerASP.NET Core, Swagger, filters, attributes
Cloud IntegrationsWorks via librariesMature SDKs, documentation, Azure Functions
Testing/ToolingFsUnit, ExpectoxUnit, NUnit, excellent IDE support

So why not define your domain and computation logic in F#, and host and expose it via C#?


🏗️ Project Structure

A clean structure looks like this:

/src
  /Shared.Domain (F#)
    - PaymentTypes.fs
    - PricingLogic.fs
  /Web.Api (C#)
    - Program.cs
    - Controllers/
  /Tests
    /Domain.Tests (F#)
    /Api.Tests (C#)
  • Shared.Domain is F#, containing pure functions, types, and domain logic
  • Web.Api is C#, referencing the F# project and exposing REST endpoints
  • Tests are split to match language domains

🧪 Sample Domain in F# (Shared.Domain)

namespace Shared.Domain

type PaymentMethod =
    | Cash
    | CreditCard of string
    | PayPal of string

module PaymentProcessor =
    let describe payment =
        match payment with
        | Cash -> "Cash payment"
        | CreditCard n -> $"Card ending in {n.Substring(n.Length - 4)}"
        | PayPal email -> $"PayPal: {email}"
  • This module is clean, testable, and doesn’t depend on ASP.NET or I/O.
  • You can easily write tests using Expecto or FsUnit.

🌐 Exposing F# Logic in ASP.NET (C# Web.Api)

In your Web.Api.csproj:

<ItemGroup>
  <ProjectReference Include="..\Shared.Domain\Shared.Domain.fsproj" />
</ItemGroup>

C# Controller Using F# Logic

using Microsoft.AspNetCore.Mvc;
using Shared.Domain;

[ApiController]
[Route("api/[controller]")]
public class PaymentController : ControllerBase
{
    [HttpGet("describe")]
    public IActionResult Describe([FromQuery] string method, [FromQuery] string? value = null)
    {
        PaymentMethod payment = method.ToLower() switch
        {
            "cash" => new PaymentMethod.Cash(),
            "creditcard" when value is not null => new PaymentMethod.CreditCard(value),
            "paypal" when value is not null => new PaymentMethod.PayPal(value),
            _ => throw new ArgumentException("Invalid input")
        };

        string description = PaymentProcessor.describe(payment);
        return Ok(description);
    }
}
  • You construct the F# discriminated union in C# using the compiled types.
  • The logic remains purely functional and testable in F#, while the API surface is idiomatic ASP.NET.

🔍 Common Gotchas and Tips

✅ Interop Tips

  • Use public modules and types in F# (namespace, module, and type).
  • Avoid F#-specific constructs (like inline functions or computation expressions) if they make interop awkward.
  • Be cautious with F#’s Option/Result types—they appear differently in C# and may require helpers.

✅ Best Practices

  • Keep your domain model IO-free (pure functions, no HttpClient, etc.).
  • Write unit tests in F# and integration tests in C#.
  • Use shared DTOs or interfaces for clean hand-off between layers if needed.

🚀 Deployment and CI/CD

In the .NET 9 world, you can:

  • Build and containerize this hybrid solution using dotnet publish
  • Deploy as one unit or split into microservices if scaling dictates
  • Write Bicep or Terraform to deploy F# logic wrapped in Azure Functions or C# APIs in App Services

Even AOT scenarios (ahead-of-time compilation) are feasible now for both languages.


📚 Summary

Combining F# and C# in a microservice architecture is a powerful strategy in .NET 9. It lets you:

BenefitDescription
Use the right tool for the jobF# for logic, C# for API/integration
Improve testabilityPure logic = easier to test
Encourage separation of concernsF# libraries force a clearer architecture
Minimize tech riskStill one platform, same tooling and deployment

This approach is clean, maintainable, and makes it easier to onboard developers from either language background. With .NET 9’s performance and deployment enhancements, you can confidently run this hybrid setup at scale.


Views: 23

🔧 Mixing F# and C# in a .NET 9 Microservice Architecture

Johannes Rest


.NET Architekt und Entwickler


Beitragsnavigation


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert