Skip to content

Instantly share code, notes, and snippets.

@atsapura
Created August 4, 2020 13:23
Show Gist options
  • Save atsapura/71b58174ba330c0843df24a485e73693 to your computer and use it in GitHub Desktop.
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.
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