次のレイヤーを持つ ASP.NET MVC アプリケーションがあるとします。
- UI (ビュー、CSS、Javascript など)
- コントローラー
- サービス (ビジネス ロジックとデータ アクセスを含む)
個別のデータ アクセス レイヤーがない理由は、SQL 型プロバイダーを使用しているためです。
(以下のコードは未加工のドラフトであるため、機能しない可能性があります)。UserService
次のように定義された名前のサービスを想像してください。
module UserService =
let getAll memoize f =
memoize(fun _ -> f)
let tryGetByID id f memoize =
memoize(fun _ -> f id)
let add evict f name keyToEvict =
let result = f name
evict keyToEvict
result
そして、Controllers レイヤーに、別のモジュールの名前をUserImpl
付けUserMemCache
ます。
module UserImpl =
let keyFor = MemCache.keyFor
let inline memoize args =
MemCache.keyForCurrent args
|> CacheHelpers.memoize0 MemCache.tryGet MemCache.store
let getAll = memoize [] |> UserService.getAll
let tryGetByID id = memoize [id] |> UserService.tryGetByID id
let add =
keyFor <@ getAll @> [id]
|> UserService.add MemCache.evict
これの使用法は次のようになります。
type UserController() =
inherit Controller()
let ctx = dbSchema.GetDataContext()
member x.GetAll() = UserImpl.getAll ctx.Users
member x.UserNumberOne = UserImpl.tryGetByID ctx.Users 1
member x.UserNumberTwo = UserImpl.tryGetByID ctx.Users 2
member x.Add(name) = UserImpl.add ctx.Users name
インターフェイスを使用すると、次の実装になります。
type UserService(ICacheProvider cacheProvider, ITable<User> db) =
member x.GetAll() =
cacheProvider.memoize(fun _ -> db |> List.ofSeq)
member x.TryGetByID id =
cacheProvider.memoize(fun _ -> db |> Query.tryFirst <@ fun z -> z.ID = ID @>)
member x.Add name =
let result = db.Add name
cacheProvider.evict <@ x.GetAll() @> []
result
使用法は次のようになります。
type UserController(ICacheProvider cacheProvider) =
inherit Controller()
let ctx = dbSchema.GetDataContext()
let userService = new UserService(cacheProvider, ctx.Users)
member x.GetAll() = userService.GetAll()
member x.UserNumberOne = userService.TryGetByID 1
member x.UserNumberTwo = userService.TryGetByID 2
明らかに、インターフェイスの実装のコードははるかに少なくなっていますが、機能的なコードとは思えません。Web アプリ全体でインターフェイスの使用を開始した場合、代わりに高階関数を使用するタイミングはいつわかりますか? - そうしないと、単純な古い OOP ソリューションになってしまいます。
要するに、いつインターフェースを使用する必要があり、いつ高階関数を使用する必要があるのでしょうか? - いくつかの線を引く必要があります。そうしないと、FP の美しさが失われるすべての型とインターフェイスになります。