Created
August 4, 2020 13:23
-
-
Save atsapura/71b58174ba330c0843df24a485e73693 to your computer and use it in GitHub Desktop.
An example of managing application dependencies with `IAppEnv` interface from a real application.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type CosmosDbConfig = | |
{ | |
Endpoint: string | |
AuthKey: string | |
} | |
type ProductBlobConfig = | |
{ | |
ConnectionString: string | |
ContainerName: string | |
} | |
type CatalogConfig = | |
{ | |
AppInsightsKey: string | |
CatalogUpdateServiceBus: string | |
CatalogDb: CosmosDbConfig | |
ProductBlob: ProductBlobConfig | |
FlushingDelay: TimeSpan | |
} | |
type ICatalogDb = | |
abstract member GetProductItem: string -> Async<ProductItem option> | |
abstract member GetItems: string seq -> Async<ProductItem[]> | |
abstract member GetPrices: string seq -> Async<ProductItemPrice[]> | |
abstract member GetInventories: string seq -> Async<Inventory[]> | |
type IProductBlob = | |
abstract member SaveProduct: ProductId -> Localized<LocalizedCompleteProduct> -> Async<Result<unit, exn>> | |
type IAppEnv = | |
abstract member CatalogDb: ICatalogDb | |
abstract member ProductBlob: IProductBlob | |
abstract member Logger: ILogger | |
abstract member Config: CatalogConfig | |
abstract member ActorSystem: ActorSystem | |
abstract member UpdateRouter: IActorRef<UpdateRouterMessage> | |
abstract member UtcNow: DateTimeOffset | |
module AppEnvironment = | |
let private getFromCosmos client (map: 'entity -> 'domain) itemId = | |
async { | |
let! doc = CosmosCatalogDb.getById<'entity> client itemId | |
return | |
match doc with | |
| None -> None | |
| Some doc -> map doc.Payload |> Some | |
} | |
type AppEnv(config: IConfiguration) as this = | |
let cosmosEndPoint = config.GetValue("CatalogDb:Endpoint") | |
let authKey = config.GetValue("CatalogDb:AuthKey") | |
let cosmosClient = CosmosCatalogDb.clientFactory(cosmosEndPoint, authKey) | |
let getFromCosmos map = getFromCosmos cosmosClient map | |
let catalogDb = | |
{ | |
new ICatalogDb with | |
member _.GetProductItem itemId = | |
getFromCosmos productItemToDomain itemId | |
member _.GetItems ids = | |
async { | |
let! docs = CosmosCatalogDb.getItems cosmosClient ids | |
return docs |> Array.map productItemToDomain | |
} | |
member _.GetInventories ids = | |
async { | |
let! entities = CosmosCatalogDb.getInventories cosmosClient ids | |
return entities |> Array.map inventoryToDomain | |
} | |
member _.GetPrices ids = | |
async { | |
let! docs = CosmosCatalogDb.getPrices cosmosClient ids | |
return docs |> Array.map priceToDomain | |
} | |
} | |
let blobConnection = config.GetValue("ProductBlob:ConnectionString") | |
let blobContainerName = config.GetValue("ProductBlob:ContainerName") | |
let blobClient = Storage.client(blobConnection, blobContainerName) | |
let blob = | |
{ | |
new IProductBlob with | |
member _.SaveProduct productId product = | |
let json = JsonConvert.SerializeObject(product, CompactCamelCaseNoFormattingSettings.settings) | |
let date = DateTimeOffset.UtcNow.ToString("yyyyMMdd_hh_mm_ss") | |
let filename = sprintf "%s_%s.json" productId date | |
Storage.write blobClient filename json | |
} | |
let appInsightsKey = config.["AppInsightsKey"] | |
let loggerFactory = LoggerFactory.Create(fun builder -> | |
builder | |
.AddConsole() | |
.AddApplicationInsights(appInsightsKey) |> ignore | |
) | |
let logger = loggerFactory.CreateLogger("CatalogUpdate") | |
let delay = config.GetValue<float>("FlushingDelay") |> TimeSpan.FromSeconds | |
let system = System.create "catalogUpdate" (Configuration.defaultConfig()) | |
let updateSorter = | |
{ | |
ItemIdMap = Dictionary<_,_>() | |
PendingUpdates = Dictionary<_,_>() | |
Children = Dictionary<_,_>() | |
} | |
|> routerLoop this | |
|> actorOf2 | |
|> props | |
|> spawn system "supervisor" | |
let serviceBusConnection = config.GetValue<string>("CatalogUpdateServiceBus") | |
let cfg = | |
{ | |
CatalogConfig.AppInsightsKey = config.GetValue<string>("AppInsightsKey") | |
CatalogUpdateServiceBus = serviceBusConnection | |
ProductBlob = | |
{ | |
ContainerName = blobContainerName | |
ConnectionString = blobConnection | |
} | |
CatalogDb = | |
{ | |
AuthKey = authKey | |
Endpoint = cosmosEndPoint | |
} | |
FlushingDelay = delay | |
} | |
interface IAppEnv with | |
member _.CatalogDb = catalogDb | |
member _.ProductBlob = blob | |
member _.Logger = logger | |
member _.Config = cfg | |
member _.ActorSystem = system | |
member _.UpdateRouter = updateSorter | |
member _.UtcNow = DateTimeOffset.UtcNow | |
let fromAppSettings(): IAppEnv = | |
let config = | |
ConfigurationBuilder() | |
.AddJsonFile("app.settings.json") | |
.AddEnvironmentVariables() | |
.Build() | |
AppEnv(config) :> IAppEnv |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment