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


























