Step 1 - Phonecat Backend Using Web Api and TypeProviders
This is step 1 of my blog series on Web Application Development in Fsharp using ASP.NET MVC
In the last blog post we have created the basic home page of phone cat with the placeholders and in this post we are going it wire the home page with the backend web apis.
As the above screenshot indicates we are going to develop three web api endpoints which serve the data to the front-end and we are going to use the existing json data available in angular-phonecat repository as our backend data store.
Initially I’ve planned to include TDD steps in this post but to keep it simple I am ignoring it. If you are interested in the tests that I’ve written check the source code.
Promotions Api
Let’s start with the promotions api which serves the data of three recently launched mobile phones. The first step is to create an ApiController called “PromotionsController”. You can create it by right clicking on the “Controllers” directory in the Web project and select Add -> Source file. In the dialog box, name it as “PromotionsController”
After creating, update the controller with the dependencies as mentioned below
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The PromotionsController
has two dependencies.
-
getPromotions
is a function a that takes a sequence of a domain modelPhoneIndex
and returns the sequence of domain modelPromotionPhone
which represents the phones that are recently launched. -
phoneIndexes
is a sequence of domain modelPhoneIndex
that represents the phones available in the backend data store.
These domain models currently not exists, so add them in the domain project as mentioned below
Right click on the domain project and select Add -> Source file. In the dialog box, name it as “Promotions”. It creates a fsharp module with the name Promotions
and add the PromotionPhone
domain model
1 2 3 4 5 6 7 8 9 |
|
Similarly, create an another source file called Production
and add the domain model PhoneIndex
1 2 3 4 5 6 7 8 9 10 |
|
The Age
property represents the age of the phone since its launch (so lower is younger!). Now the domain models are ready, the next step is to create an end point to expose it.
Let’s add a action method in PromotionsController which does the same
1 2 3 |
|
The scaffolding is ready and the next step is to add the domain method getPromotions
which does the actual business logic of giving recently launched phones.
Add the getPromotions
function in the Promotions
file as below
1 2 3 4 |
|
The getPromotions
just filters the phones with the age less than 3 and map them to the corresponding PromotionPhone
types.
Now its time to get the actual data from the data store. As I have mentioned earlier we will be using angular-phonecat repository as our data store. To access the data we will be using JsonTypeProvider.
In the DataAccess project install the nuget package FSharp.Data and add a source file TypeProviders
. Then update it with the type provider for Phone Index
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
The ToPhoneIndex
is a utility function which converts the data store model PhoneIndexTypeProvider.Root
to the domain model PhoneIndex
Now we got the type provide for PhoneIndex
, the next step is populating it from the github repository. To do that add a source file GitHubRepository
and update it as below
1 2 3 4 5 6 7 8 9 10 11 |
|
That’s it! just three line of code to get the data from the angular-phonecat repo and map to the equivalent strongly typed models
Composition Root
Now we need to wire up the controller with the domain and the data access. We can use a Dependency Injection Container to achieve it. In fact it used to be my default decision. But after reading this wonderful blog post by Mark Seeman I have actually started rethinking about dependency injection. In this sample application we are going to use the Composition Root as suggested by Mark Seeman.
In the Web project create a source file with the name Infrastructure
and add the following composition root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
We have actually created a custom implementation of IHttpControllerActivator
as mentioned here which takes care of initializing the controllers by wiring the data store and the domain functions with the controller
The only pending task is tell ASP.NET Web Api to use this composition root. Update the Global.asax.fs file as below
1 2 3 4 5 6 7 |
|
We have done an optimization by getting the phone indexes from the github when the application is starting, so that any requests to the application doesn’t get the data from github (typical enterprise performance improvement!).
Manufacturers Api
We are going to create the Manufacturers Api in the same way as Promotions Api. Let’s start from ManufacturersController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
As PromotionsController
, ManufacturersController
has also two dependencies.
-
getManufacturerNames
is a function takes a sequence of domain modelPhone
and returns the sequence of domain modelManufacturerName
which represents the manufacturer names of the given phones. -
phones
is a sequence of domain model “`Phone“ that represents the phones available in the backend data store
The Get
method in the ManufacturersController
returns the manufacturer names for the given phones
The ManufacturerViewModel
is just a DTO that is used to share the data across the wire.
The next step is to add the domain models. In the Production
file of Domain project add the following
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
As the backend data-store doesn’t directly gives the manufacturer name, we will be adding two static members that derives the manufacturer name from the phone name. The ToString
function translates to the domain model to its equivalent string version
After defining the domain models, add the business logic for the getting the manufacturer names from the phone in the new source file Phones
of the Domain project.
1 2 3 4 5 6 7 8 |
|
The getManufacturerNames
is a straight forward function that map given the phones to its equivalent manufacture names.
The next step would be fetching the phones from the data-store.
Let’s start by defining a type provider for the phone. Update the TypeProviders
module in the DataAccess project as below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
|
Like ToPhoneIndex
utility function, ToPhone
converts the data store model PhoneTypeProvider.Root
to the domain model Phone
Unlike the phone index, angular-phonecat repository stores the details of each phones in a seperate json file with the naming convention of {phoneId}.json. So, in order to get the details of all the phones, we need to fire multiple requests to get the details. Thanks to fsharp async workflow we can easily pull all the data parallelly and populate the strongly typed domain models from them.
Update the GitHubRepository
module with the code to populate the data from the data store.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Expressiveness is one of the reason behind my fsharp addiction. Just see the above code we have done some much thing with just few lines of code. We have fetched the details of all the phones using their ids from phone index and created an in-memory map.
The getPhones
function returns the strongly typed PhoneTypeProvider
models from the in-memory map.
The last thing is to wire up all the things and create the ManufacturersController
. Update the CompositionRoot
type in the Infrastructure
module with the below code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
And update the Global.asax.fs as below
1 2 3 4 5 6 7 8 |
|
Top Selling Phones Api
Create PhonesController
in the Web project and add the api to return the top selling phones
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
The getTopSellingPhones
picks given count of phones from the given phones and return them.
In the Phones
module of Domain project add the function getTopSellingPhones
1 2 3 4 5 6 |
|
The PhoneSold
domain model represents how much quantity of each phone has been sold. The getTopSellingPhones
sorts the given phones in the descending order based on the quantity being sold and return the given count of the phones from the sorting result
Right now the PhoneSold
domain model is not added. So add them in the new module Purchases
in the Domain project as below
1 2 3 4 5 6 7 8 |
|
The angular data-store doesn’t provide quantity sold information, so we need to get it from somewhere else. To keep it simple, we are going add an InMemoryInventory
in DataAccess project as below
1 2 3 4 5 6 7 8 9 10 11 12 |
|
As previous api’s we need wire things up and create the controller in the CompositeRoot
1 2 3 4 |
|
We have used partial application of the Phones.getTopSellingPhones
function here. Its signature is seq<PhoneSold> -> int -> seq<Phone> -> seq<Phone>
but the controller expects the function with the signature int -> seq<Phone> -> seq<Phone>
.
The expression
1
|
|
actually does this.
Front-End
As front-end is out of the scope of this blog post I am not covering it here. I’ve actually developed it using knockout.js.
Summary
We have covered a quite a large ground here by creating three web-api endpoints in fsharp using Web Api 2. In the later posts we will be adding error handling and making it robust. The source code for this step can be found in the github repository