Dru Sellers

Composing Application Styles

316 words · 2 minute read

C# with IoC/DI Container

  • leverage interfaces and classes
  • compose through the constructor

Service

public interface IRepository { void Save<T>(T entity); }

Implementation

public class DatabaseRepository
{
    string _connectionString;

    public DatabaseRepository(string connectionString)
    {
        _connectionString = connectionString;
    }

    public void Save<T>(T entity)
    {
        /* elided */
    }
}

Usage in Controller

public class OrderController
{
    readonly IRepository _repository;

    public OrderController(IRepository repository)
    {
      _repository = repository;
    }

    // Assume a JSON -> Order deserialization
    public ActionResult Post(Order order)
    {
        _repository.Save(order);
    }
}

What is effectively happening when we are saving an object looks like this.

new DatabaseRepository("a connection string").Save(someObject);

// with controller
new OrderController(new DatabaseRepository("a connection string")).Post(anOrder);

This reminds me a lot of how I would compose the same problem in F#.

// meta environment detection
let connString = System.Configuration.ConfigurationManager::AppSetting["dbconn"];

module DatabaseRepository
  let databaseSaveEntity (connectionString : string ) (entity : 'a) : () =
    // elided
    ()

// Done by IoC + Interface Implementation
module Repository
  let saveEntity = databaseSaveEntity connString

// based on Suave.io
module OrdersActions

  let orderPost (httpContent) -> () =
    let order = getOrder httpContent
    saveEntity order
    ()

Both of these languages are composing their functions.

(ns environment)

  (def connection-string (env "dbConn"))

(ns repository.database)
  (defn save [connection-string entity]
    (# do stuff))

(ns repository
  (:require [repository.database :as database]
            [environment]))


  (def save-entity (partial database/save environment/connection-string))

(ns http-actions
  (: require [repository :as repo]))

  (defn raw-order-post [repository-save request]
    (let [order (get-order request)]
      (repository-save order)))

  (def order-post (partial raw-order-post repository/save))

This is how you might do the same thing with Clojure, but clojure has a really nice model for composing applications called Component whose real goal is to manage the lifecycle of stateful objects.

So, now the question is. How do we do this in Rust?