liveBook · Manning

Dependency

As explained in F# in Action: A Revised Edition of Get Programming with F#

An external dependency is a system or service outside an application that its code relies on, such as a database or web service.

An external dependency is a system or service outside an application that its code relies on, such as a database, web service, filesystem, or console. Keeping domain logic separate from these dependencies makes code easier to test and easier to exercise interactively. section 1.5.6 section 15.2

Directly calling a dependency inside business logic couples the logic to one implementation. Abstracting the interaction lets tests verify greeting or validation logic independently while deployed code supplies the real printer, database, or service. section 15.2 section 15.3.1

Practical dependency-handling guidance:

  • Separate domain logic from databases, web services, filesystems, and other external systems where testing or replacement matters. section 1.5.6
  • Pass behavior such as a logging function when logic needs to emit messages, so tests can provide an alternative implementation. section 15.2
  • Avoid threading low-level details such as connection strings through every layer when only child functions require them; this increases coupling and obscures responsibilities. section 15.3.1
  • Balance decoupling against its cost when an implementation is unlikely to change. section 15.3.1

ATM output

An ATM function can calculate the correct greeting and balance message without printing directly. Production code plugs in console or filesystem output, while tests provide a controllable printer and verify the messages. section 15.2

Listing 15.6 Threading a low-level argument through several layers
module IO                                                        #1
 
let dispatchOrder
    (authToken:AuthToken)
    (order:ValidatedOrder) =                                     #2
    Ok
        {| DispatchDate = DateTime.Today.AddDays 2.
           Status = "Dispatched" |}
 
let loadCustomer
    (connectionString:SqlConnectionString)
    customerId =                                                 #2
    Ok { CustomerId = customerId; Name = "John Doe" }
 
...
 
module BusinessLogic
 
let validateOrder
    (logMsg:string -> unit)
    connectionString (order:Order) =                             #3
    logMsg "Loading customer from DB..."
    let customer =
        IO.loadCustomer
            connectionString
            order.CustomerId                                     #3
    logMsg "Validated order!"
    ValidatedOrder order
 
let processOrder logMsg authToken connectionString order =       #4
    let validatedOrder =
        validateOrder logMsg connectionString order              #3
 
    match validatedOrder with
    | Ok validatedOrder ->
           IO.dispatchOrder authToken validatedOrder
    | Error e ->
        Error e

Trace how a connection string moves from an order-processing entry point into validation and database access, revealing the coupling created by low-level arguments. section 15.3.1

How F# in Action: A Revised Edition of Get Programming with F# covers it

How deep it goes
Explains dependency coupling through ATM and order-processing examples, but stops short of prescribing one universal abstraction technique.
Examples
Uses ATM messages, logging, order validation, database loading, order dispatch, and connection-string threading as concrete examples.
What you can do after
Apply it by separating testable logic from I/O and supplying real or substitute implementations at the application boundary.
How it connects
Connects external dependencies with domain logic, unit testing, interactive scripting, function composition, and coupling.

Also explained in

Compare how 27 books explain dependency →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of F# in Action: A Revised Edition of Get Programming with F#. The section links point to the parts of the book it draws on.