diff --git a/.gitignore b/.gitignore
index 75d3a4bd2..d7f16d65c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
 *.user
 *.userosscache
 *.sln.docstates
+.vscode/
 
 # User-specific files (MonoDevelop/Xamarin Studio)
 *.userprefs
diff --git a/README.md b/README.md
index faf758f96..47a0a4da1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 # eShopOnContainers - Microservices Architecture and Containers based Reference Application (**BETA state** - Visual Studio 2017 and CLI environments compatible)
 Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers. <p>
+**Note for Pull Requests**: We accept pull request from the community. When doing it, please do it onto the DEV branch which is the consolidated work-in-progress branch. Do not request it onto Master, if possible.
 
 > ### DISCLAIMER
 > **IMPORTANT:** The current state of this sample application is **BETA**, consider it version a 0.1 foundational version, therefore, many areas could be improved and change significantly while refactoring current code and implementing new features. **Feedback with improvements and pull requests from the community will be highly appreciated and accepted.** 
diff --git a/cli-linux/build-bits-linux.sh b/cli-linux/build-bits-linux.sh
old mode 100644
new mode 100755
index e99df361b..7cda26d8c
--- a/cli-linux/build-bits-linux.sh
+++ b/cli-linux/build-bits-linux.sh
@@ -1,18 +1,17 @@
-
-projectList=(
-    "/src/Services/Catalog/Catalog.API"
-    "/src/Services/Basket/Basket.API"
-    "/src/Services/Ordering/Ordering.API"
-    "/src/Services/Identity/Identity.API"
-    "/src/Web/WebMVC"
-    "/src/Web/WebSPA"
-    "/src/Web/WebStatus
+#!/bin/bash
+declare -a projectList=(
+    '../src/Services/Catalog/Catalog.API'
+    '../src/Services/Basket/Basket.API'
+    '../src/Services/Ordering/Ordering.API'
+    '../src/Services/Identity/Identity.API'
+    '../src/Web/WebMVC'
+    '../src/Web/WebSPA'
+    '../src/Web/WebStatus'
 )
 
 # Build SPA app
-pushd $(pwd)/src/Web/WebSPA
-npm rebuild node-sass
-npm run build:prod
+# pushd $(pwd)../src/Web/WebSPA
+# npm run build:prod
 
 for project in "${projectList[@]}"
 do
@@ -28,13 +27,13 @@ do
 done
 
 # remove old docker images:
-#images=$(docker images --filter=reference="eshop/*" -q)
-#if [ -n "$images" ]; then
-#    docker rm $(docker ps -a -q) -f
-#    echo "Deleting eShop images in local Docker repo"
-#    echo $images
-#    docker rmi $(docker images --filter=reference="eshop/*" -q) -f
-#fi
+images=$(docker images --filter=reference="eshop/*" -q)
+if [ -n "$images" ]; then
+    docker rm $(docker ps -a -q) -f
+    echo "Deleting eShop images in local Docker repo"
+    echo $images
+    docker rmi $(docker images --filter=reference="eshop/*" -q) -f
+fi
 
 # No need to build the images, docker build or docker compose will
 # do that using the images and containers defined in the docker-compose.yml file.
diff --git a/docker-compose-windows.yml b/docker-compose-windows.yml
index b8eaabfc8..5a08a9302 100644
--- a/docker-compose-windows.yml
+++ b/docker-compose-windows.yml
@@ -2,7 +2,7 @@ version: '2.1'
 
 services:
   basket.api:
-    image: eshop/basket.api
+    image: eshop/basket.api-win
     build:
       context: ./src/Services/Basket/Basket.API
       dockerfile: Dockerfile.nanowin
@@ -11,7 +11,7 @@ services:
       - identity.api
 
   catalog.api:
-    image: eshop/catalog.api
+    image: eshop/catalog.api-win
     build:
       context: ./src/Services/Catalog/Catalog.API
       dockerfile: Dockerfile.nanowin
@@ -19,7 +19,7 @@ services:
       - sql.data
 
   identity.api:
-    image: eshop/identity.api
+    image: eshop/identity.api-win
     build:
       context: ./src/Services/Identity/Identity.API
       dockerfile: Dockerfile.nanowin
@@ -27,7 +27,7 @@ services:
       - sql.data
 
   ordering.api:
-    image: eshop/ordering.api
+    image: eshop/ordering.api-win
     build:
       context: ./src/Services/Ordering/Ordering.API
       dockerfile: Dockerfile.nanowin
@@ -35,7 +35,7 @@ services:
       - sql.data
 
   webspa:
-    image: eshop/webspa
+    image: eshop/webspa-win
     build:
       context: ./src/Web/WebSPA
       dockerfile: Dockerfile.nanowin
@@ -44,7 +44,7 @@ services:
       - basket.api
 
   webmvc:
-    image: eshop/webmvc
+    image: eshop/webmvc-win
     build:
       context: ./src/Web/WebMVC
       dockerfile: Dockerfile.nanowin
@@ -58,18 +58,18 @@ services:
     image: microsoft/mssql-server-windows
 
   basket.data:
-    image: redis
-    build:
-      context: ./_docker/redis
-      dockerfile: Dockerfile.nanowin
+    image: eshop/redis-win
+#    build:
+#      context: ./_docker/redis
+#      dockerfile: Dockerfile.nanowin
     ports:
         - "6379:6379"
 
   rabbitmq:
-    image: rabbitmq
-    build:
-      context: ./_docker/rabbitmq
-      dockerfile: Dockerfile.nanowin  
+    image: eshop/rabbitmq-win
+#    build:
+#      context: ./_docker/rabbitmq
+#      dockerfile: Dockerfile.nanowin  
     ports:
         - "5672:5672"
   
diff --git a/docker-compose.ci.build.yml b/docker-compose.ci.build.yml
index ef9705c8e..546b7690f 100644
--- a/docker-compose.ci.build.yml
+++ b/docker-compose.ci.build.yml
@@ -6,5 +6,5 @@ services:
     volumes:
       - .:/src
     working_dir: /src
-    command: /bin/bash -c "pushd ./src/Web/WebSPA && npm rebuild node-sass && pushd ./../../.. && dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
+    command: /bin/bash -c "dotnet restore ./eShopOnContainers-ServicesAndWebApps.sln && dotnet publish ./eShopOnContainers-ServicesAndWebApps.sln -c Release -o ./obj/Docker/publish"
 
diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln
index 53a9ceada..cf2be1c26 100644
--- a/eShopOnContainers-ServicesAndWebApps.sln
+++ b/eShopOnContainers-ServicesAndWebApps.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.26228.12
+VisualStudioVersion = 15.0.26403.7
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
 EndProject
@@ -62,18 +62,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationEventLogEF", "sr
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{A81ECBC2-6B00-4DCD-8388-469174033379}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj", "{942ED6E8-0050-495F-A0EA-01E97F63760C}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.Data", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj", "{7804FC60-23E6-490C-8E08-F9FEF829F184}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{FBF43D93-F2E7-4FF8-B4AB-186895949B88}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resilience.Http", "src\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj", "{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.SqlServer", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj", "{4BD76717-3102-4969-8C2C-BAAA3F0263B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -712,54 +714,6 @@ Global
 		{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.Build.0 = Release|Any CPU
 		{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.ActiveCfg = Release|Any CPU
 		{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|ARM.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhone.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x64.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x64.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x86.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x86.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|ARM.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhone.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x64.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x86.Build.0 = Debug|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|ARM.ActiveCfg = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|ARM.Build.0 = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhone.ActiveCfg = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhone.Build.0 = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x64.ActiveCfg = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x64.Build.0 = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x86.ActiveCfg = Release|Any CPU
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x86.Build.0 = Release|Any CPU
 		{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
 		{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
 		{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@@ -808,54 +762,6 @@ Global
 		{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.Build.0 = Release|Any CPU
 		{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.ActiveCfg = Release|Any CPU
 		{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.Build.0 = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|Any CPU.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|ARM.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|ARM.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhone.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x64.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x64.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x86.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x86.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|ARM.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhone.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhone.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x64.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x86.Build.0 = Debug|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|ARM.ActiveCfg = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|ARM.Build.0 = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhone.ActiveCfg = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhone.Build.0 = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x64.ActiveCfg = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x64.Build.0 = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x86.ActiveCfg = Release|Any CPU
-		{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x86.Build.0 = Release|Any CPU
 		{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
 		{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
 		{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@@ -952,6 +858,150 @@ Global
 		{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.Build.0 = Release|Any CPU
 		{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.ActiveCfg = Release|Any CPU
 		{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.Build.0 = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|ARM.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x64.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x64.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x86.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x86.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|ARM.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x64.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x86.Build.0 = Debug|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|Any CPU.Build.0 = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|ARM.ActiveCfg = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|ARM.Build.0 = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhone.Build.0 = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x64.ActiveCfg = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x64.Build.0 = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x86.ActiveCfg = Release|Any CPU
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x86.Build.0 = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|ARM.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x64.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x64.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x86.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x86.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|ARM.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x64.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x86.Build.0 = Debug|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|ARM.ActiveCfg = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|ARM.Build.0 = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhone.Build.0 = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.ActiveCfg = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.Build.0 = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.ActiveCfg = Release|Any CPU
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.Build.0 = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|ARM.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x64.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x64.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x86.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.AppStore|x86.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|ARM.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x64.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Debug|x86.Build.0 = Debug|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|ARM.ActiveCfg = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|ARM.Build.0 = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhone.Build.0 = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.ActiveCfg = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.Build.0 = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.ActiveCfg = Release|Any CPU
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -981,11 +1031,12 @@ Global
 		{8088F3FC-6787-45FA-A924-816EC81CBFAC} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
 		{9EE28E45-1533-472B-8267-56C48855BA0E} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
 		{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
-		{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E} = {A81ECBC2-6B00-4DCD-8388-469174033379}
 		{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
-		{7804FC60-23E6-490C-8E08-F9FEF829F184} = {A81ECBC2-6B00-4DCD-8388-469174033379}
 		{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
 		{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
 		{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
+		{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}
+		{4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379}
+		{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
 	EndGlobalSection
 EndGlobal
diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj b/src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj
new file mode 100644
index 000000000..1387a74dd
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>    
+    <TargetFramework>netcoreapp1.1</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
+    <PackageReference Include="xunit" Version="2.2.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
+    <ProjectReference Include="..\EventBus\EventBus.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs b/src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs
new file mode 100644
index 000000000..dd5f7f5b4
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs
@@ -0,0 +1,56 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
+using System;
+using System.Linq;
+using Xunit;
+
+namespace EventBus.Tests
+{
+    public class InMemory_SubscriptionManager_Tests
+    {
+        [Fact]
+        public void After_Creation_Should_Be_Empty()
+        {
+            var manager = new InMemoryEventBusSubscriptionsManager();
+            Assert.True(manager.IsEmpty);
+        }
+
+        [Fact]
+        public void After_One_Event_Subscription_Should_Contain_The_Event()
+        {
+            var manager = new InMemoryEventBusSubscriptionsManager();
+            manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
+            Assert.True(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
+        }
+
+        [Fact]
+        public void After_All_Subscriptions_Are_Deleted_Event_Should_No_Longer_Exists()
+        {
+            var manager = new InMemoryEventBusSubscriptionsManager();
+            manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
+            manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
+            Assert.False(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
+        }
+
+        [Fact]
+        public void Deleting_Last_Subscription_Should_Raise_On_Deleted_Event()
+        {
+            bool raised = false;
+            var manager = new InMemoryEventBusSubscriptionsManager();
+            manager.OnEventRemoved += (o, e) => raised = true;
+            manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
+            manager.RemoveSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void Get_Handlers_For_Event_Should_Return_All_Handlers()
+        {
+            var manager = new InMemoryEventBusSubscriptionsManager();
+            manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>(() => new TestIntegrationEventHandler());
+            manager.AddSubscription<TestIntegrationEvent, TestIntegrationOtherEventHandler>(() => new TestIntegrationOtherEventHandler());
+            var handlers = manager.GetHandlersForEvent<TestIntegrationEvent>();
+            Assert.Equal(2, handlers.Count());
+        }
+
+    }
+}
diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEvent.cs b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEvent.cs
new file mode 100644
index 000000000..a77f3ef6f
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEvent.cs
@@ -0,0 +1,11 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace EventBus.Tests
+{
+    public class TestIntegrationEvent : IntegrationEvent
+    {
+    }
+}
diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs
new file mode 100644
index 000000000..0b5b793ee
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationEventHandler.cs
@@ -0,0 +1,23 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EventBus.Tests
+{
+    public class TestIntegrationOtherEventHandler : IIntegrationEventHandler<TestIntegrationEvent>
+    {
+        public bool Handled { get; private set; }
+
+        public TestIntegrationOtherEventHandler()
+        {
+            Handled = false;
+        }
+
+        public async Task Handle(TestIntegrationEvent @event)
+        {
+            Handled = true;
+        }
+    }
+}
diff --git a/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs
new file mode 100644
index 000000000..72e1ed2cd
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus.Tests/TestIntegrationOtherEventHandler.cs
@@ -0,0 +1,23 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EventBus.Tests
+{
+    public class TestIntegrationEventHandler : IIntegrationEventHandler<TestIntegrationEvent>
+    {
+        public bool Handled { get; private set; }
+
+        public TestIntegrationEventHandler()
+        {
+            Handled = false;
+        }
+
+        public async Task Handle(TestIntegrationEvent @event)
+        {
+            Handled = true;
+        }
+    }
+}
diff --git a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs
index 63f9f1b99..9ab7a4499 100644
--- a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs
+++ b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs
@@ -1,11 +1,17 @@
 using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using System;
 
 namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
 {
     public interface IEventBus
     {
-        void Subscribe<T>(IIntegrationEventHandler<T> handler) where T: IntegrationEvent;
-        void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent;
+        void Subscribe<T, TH>(Func<TH> handler)
+            where T : IntegrationEvent
+            where TH : IIntegrationEventHandler<T>;
+        void Unsubscribe<T, TH>()
+            where TH : IIntegrationEventHandler<T>
+            where T : IntegrationEvent;
+
         void Publish(IntegrationEvent @event);
     }
 }
diff --git a/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs
new file mode 100644
index 000000000..2fdefc039
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs
@@ -0,0 +1,26 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
+{
+    public interface IEventBusSubscriptionsManager
+    {
+        bool IsEmpty { get; }
+        event EventHandler<string> OnEventRemoved;
+        void AddSubscription<T, TH>(Func<TH> handler)
+           where T : IntegrationEvent
+           where TH : IIntegrationEventHandler<T>;
+
+       void RemoveSubscription<T, TH>()
+            where TH : IIntegrationEventHandler<T>
+            where T : IntegrationEvent;
+        bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent;
+        bool HasSubscriptionsForEvent(string eventName);
+        Type GetEventTypeByName(string eventName);
+        void Clear();
+        IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent;
+        IEnumerable<Delegate> GetHandlersForEvent(string eventName);
+    }
+}
\ No newline at end of file
diff --git a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs
new file mode 100644
index 000000000..11fdba3c5
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs
@@ -0,0 +1,115 @@
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
+{
+    public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
+    {
+        private readonly Dictionary<string, List<Delegate>> _handlers;
+        private readonly List<Type> _eventTypes;
+
+        public event EventHandler<string> OnEventRemoved;
+
+        public InMemoryEventBusSubscriptionsManager()
+        {
+            _handlers = new Dictionary<string, List<Delegate>>();
+            _eventTypes = new List<Type>();
+        }
+
+        public bool IsEmpty => !_handlers.Keys.Any();
+        public void Clear() => _handlers.Clear();
+
+        public void AddSubscription<T, TH>(Func<TH> handler) 
+            where T : IntegrationEvent
+            where TH : IIntegrationEventHandler<T>
+        {
+            var key = GetEventKey<T>();
+            if (!HasSubscriptionsForEvent<T>())
+            {
+                _handlers.Add(key, new List<Delegate>());
+            }
+            _handlers[key].Add(handler);
+            _eventTypes.Add(typeof(T));
+        }
+
+        public void RemoveSubscription<T, TH>()
+            where TH : IIntegrationEventHandler<T>
+            where T : IntegrationEvent
+        {
+            var handlerToRemove = FindHandlerToRemove<T, TH>();
+            if (handlerToRemove != null)
+            {
+                var key = GetEventKey<T>();
+                _handlers[key].Remove(handlerToRemove);
+                if (!_handlers[key].Any())
+                {
+                    _handlers.Remove(key);
+                    var eventType = _eventTypes.SingleOrDefault(e => e.Name == key);
+                    if (eventType != null)
+                    {
+                        _eventTypes.Remove(eventType);
+                        RaiseOnEventRemoved(eventType.Name);
+                    }
+                }
+                
+            }
+        }
+
+        public IEnumerable<Delegate> GetHandlersForEvent<T>() where T : IntegrationEvent
+        {
+            var key = GetEventKey<T>();
+            return GetHandlersForEvent(key);
+        }
+        public IEnumerable<Delegate> GetHandlersForEvent(string eventName) => _handlers[eventName];
+
+        private void RaiseOnEventRemoved(string eventName)
+        {
+            var handler = OnEventRemoved;
+            if (handler != null)
+            {
+                OnEventRemoved(this, eventName);
+            }
+        }
+
+        private Delegate FindHandlerToRemove<T, TH>()
+            where T : IntegrationEvent
+            where TH : IIntegrationEventHandler<T>
+        {
+            if (!HasSubscriptionsForEvent<T>())
+            {
+                return null;
+            }
+
+            var key = GetEventKey<T>();
+            foreach (var func in _handlers[key])
+            {
+                var genericArgs = func.GetType().GetGenericArguments();
+                if (genericArgs.SingleOrDefault() == typeof(TH))
+                {
+                    return func;
+                }
+            }
+
+            return null;
+        }
+
+        public bool HasSubscriptionsForEvent<T>() where T : IntegrationEvent
+        {
+            var key = GetEventKey<T>();
+            return HasSubscriptionsForEvent(key);
+        }
+        public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName);
+       
+        public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName);
+
+        private string GetEventKey<T>()
+        {
+            return typeof(T).Name;
+        }
+    }
+}
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs
index 894afb4e4..0aafaf90a 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs
@@ -10,18 +10,18 @@ using System.Net.Sockets;
 
 namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
 {
-    public class DefaultRabbitMQPersisterConnection
-       : IRabbitMQPersisterConnection
+    public class DefaultRabbitMQPersistentConnection
+       : IRabbitMQPersistentConnection
     {
         private readonly IConnectionFactory _connectionFactory;
-        private readonly ILogger<DefaultRabbitMQPersisterConnection> _logger;
+        private readonly ILogger<DefaultRabbitMQPersistentConnection> _logger;
 
         IConnection _connection;
         bool _disposed;
 
         object sync_root = new object();
 
-        public DefaultRabbitMQPersisterConnection(IConnectionFactory connectionFactory,ILogger<DefaultRabbitMQPersisterConnection> logger)
+        public DefaultRabbitMQPersistentConnection(IConnectionFactory connectionFactory,ILogger<DefaultRabbitMQPersistentConnection> logger)
         {
             _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
             _logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -87,13 +87,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
                     _connection.CallbackException += OnCallbackException;
                     _connection.ConnectionBlocked += OnConnectionBlocked;
 
-                    _logger.LogInformation($"RabbitMQ persister connection acquire a connection {_connection.Endpoint.HostName} and is subscribed to failure events");
+                    _logger.LogInformation($"RabbitMQ persistent connection acquired a connection {_connection.Endpoint.HostName} and is subscribed to failure events");
                  
                     return true;
                 }
                 else
                 {
-                    _logger.LogCritical("FATAL ERROR: RabbitMQ connections can't be created and opened");
+                    _logger.LogCritical("FATAL ERROR: RabbitMQ connections could not be created and opened");
 
                     return false;
                 }
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
index e7a493c10..adbc52ad1 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
@@ -1,4 +1,5 @@
-using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
 using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
 using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
@@ -21,32 +22,50 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
     {
         const string BROKER_NAME = "eshop_event_bus";
 
-        private readonly IRabbitMQPersisterConnection _persisterConnection;
+        private readonly IRabbitMQPersistentConnection _persistentConnection;
         private readonly ILogger<EventBusRabbitMQ> _logger;
-
-        private readonly Dictionary<string, List<IIntegrationEventHandler>> _handlers
-            = new Dictionary<string, List<IIntegrationEventHandler>>();
-
-        private readonly List<Type> _eventTypes
-            = new List<Type>();
+        private readonly IEventBusSubscriptionsManager _subsManager;
+             
 
         private IModel _consumerChannel;
         private string _queueName;
 
-        public EventBusRabbitMQ(IRabbitMQPersisterConnection persisterConnection, ILogger<EventBusRabbitMQ> logger)
+        public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger, IEventBusSubscriptionsManager subsManager)
         {
-            _persisterConnection = persisterConnection ?? throw new ArgumentNullException(nameof(persisterConnection));
+            _persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
             _logger = logger ?? throw new ArgumentNullException(nameof(logger));
-
+            _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
             _consumerChannel = CreateConsumerChannel();
+
+            _subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
         }
 
+        private void SubsManager_OnEventRemoved(object sender, string eventName)
+        {
+            if (!_persistentConnection.IsConnected)
+            {
+                _persistentConnection.TryConnect();
+            }
+
+            using (var channel = _persistentConnection.CreateModel())
+            {
+                channel.QueueUnbind(queue: _queueName,
+                    exchange: BROKER_NAME,
+                    routingKey: eventName);
+
+                if (_subsManager.IsEmpty)
+                {
+                    _queueName = string.Empty;
+                    _consumerChannel.Close();
+                }
+            }
+        }
 
         public void Publish(IntegrationEvent @event)
         {
-            if (!_persisterConnection.IsConnected)
+            if (!_persistentConnection.IsConnected)
             {
-                _persisterConnection.TryConnect();
+                _persistentConnection.TryConnect();
             }
 
             var policy = RetryPolicy.Handle<BrokerUnreachableException>()
@@ -56,7 +75,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
                     _logger.LogWarning(ex.ToString());
                 });
 
-            using (var channel = _persisterConnection.CreateModel())
+            using (var channel = _persistentConnection.CreateModel())
             {
                 var eventName = @event.GetType()
                     .Name;
@@ -77,75 +96,49 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
             }
         }
 
-        public void Subscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
+        public void Subscribe<T, TH>(Func<TH> handler)
+            where T : IntegrationEvent
+            where TH : IIntegrationEventHandler<T>
         {
             var eventName = typeof(T).Name;
-
-            if (_handlers.ContainsKey(eventName))
+            var containsKey = _subsManager.HasSubscriptionsForEvent<T>();
+            if (!containsKey)
             {
-                _handlers[eventName].Add(handler);
-            }
-            else
-            {
-                if (!_persisterConnection.IsConnected)
+                if (!_persistentConnection.IsConnected)
                 {
-                    _persisterConnection.TryConnect();
+                    _persistentConnection.TryConnect();
                 }
 
-                using (var channel = _persisterConnection.CreateModel())
+                using (var channel = _persistentConnection.CreateModel())
                 {
                     channel.QueueBind(queue: _queueName,
                                       exchange: BROKER_NAME,
                                       routingKey: eventName);
-
-                    _handlers.Add(eventName, new List<IIntegrationEventHandler>());
-                    _handlers[eventName].Add(handler);
-                    _eventTypes.Add(typeof(T));
                 }
-
             }
 
+            _subsManager.AddSubscription<T, TH>(handler);
+
         }
 
-        public void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
+        public void Unsubscribe<T, TH>()
+            where TH : IIntegrationEventHandler<T>
+            where T : IntegrationEvent
         {
-            var eventName = typeof(T).Name;
+            _subsManager.RemoveSubscription<T, TH>();
+        }
 
-            if (_handlers.ContainsKey(eventName) && _handlers[eventName].Contains(handler))
+        private static Func<IIntegrationEventHandler> FindHandlerByType(Type handlerType, IEnumerable<Func<IIntegrationEventHandler>> handlers)
+        {
+            foreach (var func in handlers)
             {
-                _handlers[eventName].Remove(handler);
-
-                if (_handlers[eventName].Count == 0)
+                if (func.GetMethodInfo().ReturnType == handlerType)
                 {
-                    _handlers.Remove(eventName);
-
-                    var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName);
-
-                    if (eventType != null)
-                    {
-                        _eventTypes.Remove(eventType);
-
-                        if (!_persisterConnection.IsConnected)
-                        {
-                            _persisterConnection.TryConnect();
-                        }
-
-                        using (var channel = _persisterConnection.CreateModel())
-                        {
-                            channel.QueueUnbind(queue: _queueName,
-                                exchange: BROKER_NAME,
-                                routingKey: eventName);
-
-                            if (_handlers.Keys.Count == 0)
-                            {
-                                _queueName = string.Empty;
-
-                                _consumerChannel.Close();
-                            }
-                        }
-                    }
+                    return func;
                 }
             }
+
+            return null;
         }
 
         public void Dispose()
@@ -154,18 +147,18 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
             {
                 _consumerChannel.Dispose();
             }
-            
-            _handlers.Clear();
+
+            _subsManager.Clear();
         }
 
         private IModel CreateConsumerChannel()
         {
-            if (!_persisterConnection.IsConnected)
+            if (!_persistentConnection.IsConnected)
             {
-                _persisterConnection.TryConnect();
+                _persistentConnection.TryConnect();
             }
 
-            var channel = _persisterConnection.CreateModel();
+            var channel = _persistentConnection.CreateModel();
 
             channel.ExchangeDeclare(exchange: BROKER_NAME,
                                  type: "direct");
@@ -196,15 +189,17 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
 
         private async Task ProcessEvent(string eventName, string message)
         {
-            if (_handlers.ContainsKey(eventName))
-            {
-                Type eventType = _eventTypes.Single(t => t.Name == eventName);
-                var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
-                var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
-                var handlers = _handlers[eventName];
 
-                foreach (var handler in handlers)
+            if (_subsManager.HasSubscriptionsForEvent(eventName))
+            { 
+                var eventType = _subsManager.GetEventTypeByName(eventName);
+                var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
+                var handlers = _subsManager.GetHandlersForEvent(eventName);
+
+                foreach (var handlerfactory in handlers)
                 {
+                    var handler = handlerfactory.DynamicInvoke();
+                    var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
                     await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
                 }
             }
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs
index b9debe743..5893791c5 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs
@@ -3,8 +3,7 @@ using System;
 
 namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
 {
-
-    public interface IRabbitMQPersisterConnection
+    public interface IRabbitMQPersistentConnection
         : IDisposable
     {
         bool IsConnected { get; }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs
index 64b4f48c2..f8e68c957 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs
@@ -1,6 +1,8 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
+using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Features;
@@ -11,30 +13,34 @@ namespace Microsoft.AspNetCore.HealthChecks
 {
     public class HealthCheckMiddleware
     {
-        private RequestDelegate _next;
-        private string _path;
-        private int? _port;
-        private IHealthCheckService _service;
+        private readonly RequestDelegate _next;
+        private readonly string _path;
+        private readonly int? _port;
+        private readonly IHealthCheckService _service;
+        private readonly TimeSpan _timeout;
 
-        public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port)
+        public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port, TimeSpan timeout)
         {
             _port = port;
             _service = service;
             _next = next;
+            _timeout = timeout;
         }
 
-        public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path)
+        public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path, TimeSpan timeout)
         {
             _path = path;
             _service = service;
             _next = next;
+            _timeout = timeout;
         }
 
         public async Task Invoke(HttpContext context)
         {
             if (IsHealthCheckRequest(context))
             {
-                var result = await _service.CheckHealthAsync();
+                var timeoutTokenSource = new CancellationTokenSource(_timeout);
+                var result = await _service.CheckHealthAsync(timeoutTokenSource.Token);
                 var status = result.CheckStatus;
 
                 if (status != CheckStatus.Healthy)
@@ -60,7 +66,9 @@ namespace Microsoft.AspNetCore.HealthChecks
             }
 
             if (context.Request.Path == _path)
+            {
                 return true;
+            }
 
             return false;
         }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs
index 4aa91b070..cac4b1188 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs
@@ -11,15 +11,18 @@ namespace Microsoft.AspNetCore.HealthChecks
     {
         private string _path;
         private int? _port;
+        private TimeSpan _timeout;
 
-        public HealthCheckStartupFilter(int port)
+        public HealthCheckStartupFilter(int port, TimeSpan timeout)
         {
             _port = port;
+            _timeout = timeout;
         }
 
-        public HealthCheckStartupFilter(string path)
+        public HealthCheckStartupFilter(string path, TimeSpan timeout)
         {
             _path = path;
+            _timeout = timeout;
         }
 
         public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
@@ -27,9 +30,13 @@ namespace Microsoft.AspNetCore.HealthChecks
             return app =>
             {
                 if (_port.HasValue)
-                    app.UseMiddleware<HealthCheckMiddleware>(_port);
+                {
+                    app.UseMiddleware<HealthCheckMiddleware>(_port, _timeout);
+                }
                 else
-                    app.UseMiddleware<HealthCheckMiddleware>(_path);
+                {
+                    app.UseMiddleware<HealthCheckMiddleware>(_path, _timeout);
+                }
 
                 next(app);
             };
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs
index 0b806ca06..467293137 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs
@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
 using Microsoft.AspNetCore.HealthChecks;
 using Microsoft.Extensions.DependencyInjection;
 
@@ -8,28 +9,38 @@ namespace Microsoft.AspNetCore.Hosting
 {
     public static class HealthCheckWebHostBuilderExtension
     {
+        public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
+
         public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port)
+            => UseHealthChecks(builder, port, DefaultTimeout);
+
+        public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port, TimeSpan timeout)
         {
-            Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535");
+            Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535.");
+            Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span.");
 
             builder.ConfigureServices(services =>
             {
                 var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
                 builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}");
 
-                services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port));
+                services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port, timeout));
             });
             return builder;
         }
 
         public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path)
+            => UseHealthChecks(builder, path, DefaultTimeout);
+
+        public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path, TimeSpan timeout)
         {
             Guard.ArgumentNotNull(nameof(path), path);
             // REVIEW: Is there a better URL path validator somewhere?
-            Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values");
-            Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with /");
+            Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values.");
+            Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with '/'.");
+            Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span.");
 
-            builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path)));
+            builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path, timeout)));
             return builder;
         }
     }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/HealthCheckBuilderDataExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs
similarity index 94%
rename from src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/HealthCheckBuilderDataExtensions.cs
rename to src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs
index e209dfdaf..4998c91ed 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/HealthCheckBuilderDataExtensions.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs
@@ -7,7 +7,7 @@ using System.Data.SqlClient;
 
 namespace Microsoft.Extensions.HealthChecks
 {
-    public static class HealthCheckBuilderDataExtensions
+    public static class HealthCheckBuilderSqlServerExtensions
     {
         public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString)
         {
@@ -33,7 +33,7 @@ namespace Microsoft.Extensions.HealthChecks
                         }
                     }
                 }
-                catch(Exception ex)
+                catch (Exception ex)
                 {
                     return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}");
                 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/Microsoft.Extensions.HealthChecks.Data.csproj b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj
similarity index 100%
rename from src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/Microsoft.Extensions.HealthChecks.Data.csproj
rename to src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs
new file mode 100644
index 000000000..39ed087eb
--- /dev/null
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs
@@ -0,0 +1,109 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.Extensions.HealthChecks
+{
+    public abstract class CachedHealthCheck
+    {
+        private static readonly TypeInfo HealthCheckTypeInfo = typeof(IHealthCheck).GetTypeInfo();
+
+        private volatile int _writerCount;
+
+        public CachedHealthCheck(string name, TimeSpan cacheDuration)
+        {
+            Guard.ArgumentNotNullOrEmpty(nameof(name), name);
+            Guard.ArgumentValid(cacheDuration.TotalMilliseconds >= 0, nameof(cacheDuration), "Cache duration must be zero (disabled) or greater than zero.");
+
+            Name = name;
+            CacheDuration = cacheDuration;
+        }
+
+        public IHealthCheckResult CachedResult { get; internal set; }
+
+        public TimeSpan CacheDuration { get; }
+
+        public DateTimeOffset CacheExpiration { get; internal set; }
+
+        public string Name { get; }
+
+        protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
+
+        protected abstract IHealthCheck Resolve(IServiceProvider serviceProvider);
+
+        public async ValueTask<IHealthCheckResult> RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            while (CacheExpiration <= UtcNow)
+            {
+                // Can't use a standard lock here because of async, so we'll use this flag to determine when we should write a value,
+                // and the waiters who aren't allowed to write will just spin wait for the new value.
+                if (Interlocked.Exchange(ref _writerCount, 1) != 0)
+                {
+                    await Task.Delay(5, cancellationToken).ConfigureAwait(false);
+                    continue;
+                }
+
+                try
+                {
+                    var check = Resolve(serviceProvider);
+                    CachedResult = await check.CheckAsync(cancellationToken);
+                }
+                catch (OperationCanceledException)
+                {
+                    CachedResult = HealthCheckResult.Unhealthy("The health check operation timed out");
+                }
+                catch (Exception ex)
+                {
+                    CachedResult = HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}");
+                }
+
+                CacheExpiration = UtcNow + CacheDuration;
+                _writerCount = 0;
+                break;
+            }
+
+            return CachedResult;
+        }
+
+        public static CachedHealthCheck FromHealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck)
+        {
+            Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
+
+            return new TypeOrHealthCheck_HealthCheck(name, cacheDuration, healthCheck);
+        }
+
+        public static CachedHealthCheck FromType(string name, TimeSpan cacheDuration, Type healthCheckType)
+        {
+            Guard.ArgumentNotNull(nameof(healthCheckType), healthCheckType);
+            Guard.ArgumentValid(HealthCheckTypeInfo.IsAssignableFrom(healthCheckType.GetTypeInfo()), nameof(healthCheckType), $"Health check must implement '{typeof(IHealthCheck).FullName}'.");
+
+            return new TypeOrHealthCheck_Type(name, cacheDuration, healthCheckType);
+        }
+
+        class TypeOrHealthCheck_HealthCheck : CachedHealthCheck
+        {
+            private readonly IHealthCheck _healthCheck;
+
+            public TypeOrHealthCheck_HealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck) : base(name, cacheDuration)
+                => _healthCheck = healthCheck;
+
+            protected override IHealthCheck Resolve(IServiceProvider serviceProvider) => _healthCheck;
+        }
+
+        class TypeOrHealthCheck_Type : CachedHealthCheck
+        {
+            private readonly Type _healthCheckType;
+
+            public TypeOrHealthCheck_Type(string name, TimeSpan cacheDuration, Type healthCheckType) : base(name, cacheDuration)
+                => _healthCheckType = healthCheckType;
+
+            protected override IHealthCheck Resolve(IServiceProvider serviceProvider)
+                => (IHealthCheck)serviceProvider.GetRequiredService(_healthCheckType);
+        }
+    }
+}
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs
new file mode 100644
index 000000000..2c3388709
--- /dev/null
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs
@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Extensions.HealthChecks
+{
+    public static class CachedHealthCheckExtensions
+    {
+        public static ValueTask<IHealthCheckResult> RunAsync(this CachedHealthCheck check, IServiceProvider serviceProvider)
+        {
+            Guard.ArgumentNotNull(nameof(check), check);
+
+            return check.RunAsync(serviceProvider, CancellationToken.None);
+        }
+    }
+}
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs
index cd10fb93d..5b7b49af0 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs
@@ -15,96 +15,102 @@ namespace Microsoft.Extensions.HealthChecks
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromCheck(check, builder.DefaultCacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromCheck(check, builder.DefaultCacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check, TimeSpan cacheDuration)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromCheck(check, cacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromCheck(check, cacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromTaskCheck(check, builder.DefaultCacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromTaskCheck(check, builder.DefaultCacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromTaskCheck(check, cacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration);
         }
 
         public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromTaskCheck(check, cacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration);
         }
 
         public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, builder.DefaultCacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration);
         }
 
         public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, builder.DefaultCacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration);
         }
 
         public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, cacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration);
         }
 
         public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
 
-            builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, cacheDuration));
-            return builder;
+            return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration);
+        }
+
+        // IHealthCheck versions of AddCheck
+
+        public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string checkName, IHealthCheck check)
+        {
+            Guard.ArgumentNotNull(nameof(builder), builder);
+
+            return builder.AddCheck(checkName, check, builder.DefaultCacheDuration);
+        }
+
+        // Type versions of AddCheck
+
+        public static HealthCheckBuilder AddCheck<TCheck>(this HealthCheckBuilder builder, string name) where TCheck : class, IHealthCheck
+        {
+            Guard.ArgumentNotNull(nameof(builder), builder);
+
+            return builder.AddCheck<TCheck>(name, builder.DefaultCacheDuration);
         }
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs
index cb97eec46..f3c795629 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs
@@ -14,7 +14,7 @@ namespace Microsoft.Extensions.HealthChecks
             where T : IComparable<T>
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
-            Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
+            Guard.ArgumentNotNullOrEmpty(nameof(name), name);
             Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
 
             builder.AddCheck(name, () =>
@@ -23,7 +23,7 @@ namespace Microsoft.Extensions.HealthChecks
                 var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
                 return HealthCheckResult.FromStatus(
                     status,
-                    $"{name}: min={minValue}, current={currentValue}",
+                    $"min={minValue}, current={currentValue}",
                     new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } }
                 );
             });
@@ -35,16 +35,16 @@ namespace Microsoft.Extensions.HealthChecks
             where T : IComparable<T>
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
-            Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
+            Guard.ArgumentNotNullOrEmpty(nameof(name), name);
             Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
 
-            builder.AddCheck($"{name}", () =>
+            builder.AddCheck(name, () =>
             {
                 var currentValue = currentValueFunc();
                 var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
                 return HealthCheckResult.FromStatus(
                     status,
-                    $"{name}: max={maxValue}, current={currentValue}",
+                    $"max={maxValue}, current={currentValue}",
                     new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } }
                 );
             });
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs
index 6ab393547..d7df58def 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs
@@ -2,8 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Net.Http;
 using System.Threading.Tasks;
 using Microsoft.Extensions.HealthChecks.Internal;
@@ -37,73 +35,12 @@ namespace Microsoft.Extensions.HealthChecks
                                                      Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
         {
             Guard.ArgumentNotNull(nameof(builder), builder);
-            Guard.ArgumentNotNullOrWhitespace(nameof(url), url);
+            Guard.ArgumentNotNullOrEmpty(nameof(url), url);
             Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
 
             var urlCheck = new UrlChecker(checkFunc, url);
             builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync());
             return builder;
         }
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName)
-            => AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => UrlChecker.DefaultUrlCheck(response));
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
-        {
-            Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
-
-            return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
-        }
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
-        {
-            Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
-
-            return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
-        }
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
-        {
-            Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
-
-            return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => checkFunc(response));
-        }
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      CheckStatus partialSuccessStatus)
-            => AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => UrlChecker.DefaultUrlCheck(response));
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      CheckStatus partialSuccessStatus, Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
-        {
-            Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
-
-            return AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
-        }
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      CheckStatus partialSuccessStatus, Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
-        {
-            Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
-
-            return AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
-        }
-
-        public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
-                                                      CheckStatus partialSuccessStatus, Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
-        {
-            var urls = urlItems?.ToArray();
-
-            Guard.ArgumentNotNull(nameof(builder), builder);
-            Guard.ArgumentNotNullOrEmpty(nameof(urlItems), urls);
-            Guard.ArgumentNotNullOrWhitespace(nameof(groupName), groupName);
-
-            var urlChecker = new UrlChecker(checkFunc, urls) { PartiallyHealthyStatus = partialSuccessStatus };
-            builder.AddCheck($"UrlChecks({groupName})", () => urlChecker.CheckAsync());
-            return builder;
-        }
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs
index 5a3367843..6894ce85f 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs
@@ -7,7 +7,6 @@ using System.Linq;
 
 namespace Microsoft.Extensions.HealthChecks
 {
-    // REVIEW: Does this need to be thread safe?
     /// <summary>
     /// Represents a composite health check result built from several results.
     /// </summary>
@@ -31,17 +30,23 @@ namespace Microsoft.Extensions.HealthChecks
             {
                 var checkStatuses = new HashSet<CheckStatus>(_results.Select(x => x.Value.CheckStatus));
                 if (checkStatuses.Count == 0)
+                {
                     return _initialStatus;
+                }
                 if (checkStatuses.Count == 1)
+                {
                     return checkStatuses.First();
+                }
                 if (checkStatuses.Contains(CheckStatus.Healthy))
+                {
                     return _partiallyHealthyStatus;
+                }
 
                 return CheckStatus.Unhealthy;
             }
         }
 
-        public string Description => string.Join(Environment.NewLine, _results.Select(r => r.Value.Description));
+        public string Description => string.Join(Environment.NewLine, _results.Select(r => $"{r.Key}: {r.Value.Description}"));
 
         public IReadOnlyDictionary<string, object> Data
         {
@@ -58,23 +63,21 @@ namespace Microsoft.Extensions.HealthChecks
 
         public IReadOnlyDictionary<string, IHealthCheckResult> Results => _results;
 
-        // REVIEW: Should description be required? Seems redundant for success checks.
-
         public void Add(string name, CheckStatus status, string description)
             => Add(name, status, description, null);
 
         public void Add(string name, CheckStatus status, string description, Dictionary<string, object> data)
         {
-            Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
-            Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add unknown status to composite health check result");
-            Guard.ArgumentNotNullOrWhitespace(nameof(description), description);
+            Guard.ArgumentNotNullOrEmpty(nameof(name), name);
+            Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add 'Unknown' status to composite health check result.");
+            Guard.ArgumentNotNullOrEmpty(nameof(description), description);
 
             _results.Add(name, HealthCheckResult.FromStatus(status, description, data));
         }
 
         public void Add(string name, IHealthCheckResult checkResult)
         {
-            Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
+            Guard.ArgumentNotNullOrEmpty(nameof(name), name);
             Guard.ArgumentNotNull(nameof(checkResult), checkResult);
 
             _results.Add(name, checkResult);
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs
index d068a7d05..5e1caa2ff 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs
@@ -9,68 +9,34 @@ namespace Microsoft.Extensions.HealthChecks
 {
     public class HealthCheck : IHealthCheck
     {
-        private DateTimeOffset _cacheExpiration;
-        private IHealthCheckResult _cachedResult;
-        private volatile int _writerCount;
-
-        protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
+        protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
         {
             Guard.ArgumentNotNull(nameof(check), check);
-            Guard.ArgumentValid(cacheDuration >= TimeSpan.Zero, nameof(cacheDuration), "Cache duration must either be zero (disabled) or a positive value");
 
             Check = check;
-            CacheDuration = cacheDuration;
         }
 
-        public TimeSpan CacheDuration { get; }
-
         protected Func<CancellationToken, ValueTask<IHealthCheckResult>> Check { get; }
 
-        protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
+        public ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken))
+            => Check(cancellationToken);
 
-        public async ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken)
-        {
-            while (_cacheExpiration <= UtcNow)
-            {
-                // Can't use a standard lock here because of async, so we'll use this flag to determine when we should write a value,
-                // and the waiters who aren't allowed to write will just spin wait for the new value.
-                if (Interlocked.Exchange(ref _writerCount, 1) != 0)
-                {
-                    await Task.Delay(5, cancellationToken).ConfigureAwait(false);
-                    continue;
-                }
+        public static HealthCheck FromCheck(Func<IHealthCheckResult> check)
+            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()));
 
-                try
-                {
-                    _cachedResult = await Check(cancellationToken).ConfigureAwait(false);
-                    _cacheExpiration = UtcNow + CacheDuration;
-                    break;
-                }
-                finally
-                {
-                    _writerCount = 0;
-                }
-            }
+        public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check)
+            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)));
 
-            return _cachedResult;
-        }
+        public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check)
+            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()));
 
-        public static HealthCheck FromCheck(Func<IHealthCheckResult> check, TimeSpan cacheDuration)
-            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()), cacheDuration);
+        public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check)
+            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)));
 
-        public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
-            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)), cacheDuration);
+        public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check)
+            => new HealthCheck(token => check());
 
-        public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
-            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()), cacheDuration);
-
-        public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
-            => new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)), cacheDuration);
-
-        public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
-            => new HealthCheck(token => check(), cacheDuration);
-
-        public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
-            => new HealthCheck(check, cacheDuration);
+        public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
+            => new HealthCheck(check);
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs
index f6cc17304..006e4a6ef 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs
@@ -8,33 +8,128 @@ namespace Microsoft.Extensions.HealthChecks
 {
     public class HealthCheckBuilder
     {
-        private readonly Dictionary<string, IHealthCheck> _checks;
+        private readonly Dictionary<string, CachedHealthCheck> _checksByName;
+        private readonly HealthCheckGroup _currentGroup;
+        private readonly Dictionary<string, HealthCheckGroup> _groups;
 
         public HealthCheckBuilder()
         {
-            _checks = new Dictionary<string, IHealthCheck>(StringComparer.OrdinalIgnoreCase);
+            _checksByName = new Dictionary<string, CachedHealthCheck>(StringComparer.OrdinalIgnoreCase);
+            _currentGroup = new HealthCheckGroup(string.Empty, CheckStatus.Unhealthy);
+            _groups = new Dictionary<string, HealthCheckGroup>(StringComparer.OrdinalIgnoreCase)
+            {
+                [string.Empty] = _currentGroup
+            };
+
             DefaultCacheDuration = TimeSpan.FromMinutes(5);
         }
 
-        public IReadOnlyDictionary<string, IHealthCheck> Checks => _checks;
+        /// <summary>
+        /// This constructor should only be used when creating a grouped health check builder.
+        /// </summary>
+        public HealthCheckBuilder(HealthCheckBuilder rootBuilder, HealthCheckGroup currentGroup)
+        {
+            Guard.ArgumentNotNull(nameof(rootBuilder), rootBuilder);
+            Guard.ArgumentNotNull(nameof(currentGroup), currentGroup);
 
+            _checksByName = rootBuilder._checksByName;
+            _currentGroup = currentGroup;
+            _groups = rootBuilder._groups;
+
+            DefaultCacheDuration = rootBuilder.DefaultCacheDuration;
+        }
+
+        /// <summary>
+        /// Gets the registered checks, indexed by check name.
+        /// </summary>
+        public IReadOnlyDictionary<string, CachedHealthCheck> ChecksByName => _checksByName;
+
+        /// <summary>
+        /// Gets the current default cache duration used when registering checks.
+        /// </summary>
         public TimeSpan DefaultCacheDuration { get; private set; }
 
-        public HealthCheckBuilder AddCheck(string name, IHealthCheck check)
-        {
-            Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
-            Guard.ArgumentNotNull(nameof(check), check);
+        /// <summary>
+        /// Gets the registered groups, indexed by group name. The root group's name is <see cref="string.Empty"/>.
+        /// </summary>
+        public IReadOnlyDictionary<string, HealthCheckGroup> Groups => _groups;
+
+        /// <summary>
+        /// Registers a health check type that will later be resolved via dependency
+        /// injection.
+        /// </summary>
+        public HealthCheckBuilder AddCheck<TCheck>(string checkName, TimeSpan cacheDuration) where TCheck : class, IHealthCheck
+        {
+            Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName);
+            Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered.");
+
+            var namedCheck = CachedHealthCheck.FromType(checkName, cacheDuration, typeof(TCheck));
+
+            _checksByName.Add(checkName, namedCheck);
+            _currentGroup.ChecksInternal.Add(namedCheck);
+
+            return this;
+        }
+
+        /// <summary>
+        /// Registers a concrete health check to the builder.
+        /// </summary>
+        public HealthCheckBuilder AddCheck(string checkName, IHealthCheck check, TimeSpan cacheDuration)
+        {
+            Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName);
+            Guard.ArgumentNotNull(nameof(check), check);
+            Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered.");
+
+            var namedCheck = CachedHealthCheck.FromHealthCheck(checkName, cacheDuration, check);
+
+            _checksByName.Add(checkName, namedCheck);
+            _currentGroup.ChecksInternal.Add(namedCheck);
+
+            return this;
+        }
+
+        /// <summary>
+        /// Creates a new health check group, to which you can add one or more health
+        /// checks. Uses <see cref="CheckStatus.Unhealthy"/> when the group is
+        /// partially successful.
+        /// </summary>
+        public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action<HealthCheckBuilder> groupChecks)
+            => AddHealthCheckGroup(groupName, groupChecks, CheckStatus.Unhealthy);
+
+        /// <summary>
+        /// Creates a new health check group, to which you can add one or more health
+        /// checks.
+        /// </summary>
+        public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action<HealthCheckBuilder> groupChecks, CheckStatus partialSuccessStatus)
+        {
+            Guard.ArgumentNotNullOrEmpty(nameof(groupName), groupName);
+            Guard.ArgumentNotNull(nameof(groupChecks), groupChecks);
+            Guard.ArgumentValid(partialSuccessStatus != CheckStatus.Unknown, nameof(partialSuccessStatus), "Check status 'Unknown' is not valid for partial success.");
+            Guard.ArgumentValid(!_groups.ContainsKey(groupName), nameof(groupName), $"A group with name '{groupName}' has already been registered.");
+            Guard.OperationValid(_currentGroup.GroupName == string.Empty, "Nested groups are not supported by HealthCheckBuilder.");
+
+            var group = new HealthCheckGroup(groupName, partialSuccessStatus);
+            _groups.Add(groupName, group);
+
+            var innerBuilder = new HealthCheckBuilder(this, group);
+            groupChecks(innerBuilder);
 
-            _checks.Add(name, check);
             return this;
         }
 
         public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration)
         {
-            Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration");
+            Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration.");
 
             DefaultCacheDuration = duration;
             return this;
         }
+
+        public HealthCheckBuilder WithPartialSuccessStatus(CheckStatus partiallyHealthyStatus)
+        {
+            _currentGroup.PartiallyHealthyStatus = partiallyHealthyStatus;
+
+            return this;
+        }
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckExtensions.cs
deleted file mode 100644
index 2669afc77..000000000
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckExtensions.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.Extensions.HealthChecks
-{
-    public static class HealthCheckExtensions
-    {
-        public static ValueTask<IHealthCheckResult> CheckAsync(this IHealthCheck healthCheck)
-        {
-            Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
-
-            return healthCheck.CheckAsync(CancellationToken.None);
-        }
-    }
-}
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs
new file mode 100644
index 000000000..18c55132b
--- /dev/null
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs
@@ -0,0 +1,37 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.HealthChecks
+{
+    public class HealthCheckGroup
+    {
+        private CheckStatus _partialSuccessStatus;
+
+        public HealthCheckGroup(string groupName, CheckStatus partialSuccessStatus)
+        {
+            Guard.ArgumentNotNull(nameof(groupName), groupName);
+
+            GroupName = groupName;
+            PartiallyHealthyStatus = partialSuccessStatus;
+        }
+
+        public IReadOnlyList<CachedHealthCheck> Checks => ChecksInternal.AsReadOnly();
+
+        internal List<CachedHealthCheck> ChecksInternal { get; } = new List<CachedHealthCheck>();
+
+        public string GroupName { get; }
+
+        public CheckStatus PartiallyHealthyStatus
+        {
+            get => _partialSuccessStatus;
+            internal set
+            {
+                Guard.ArgumentValid(value != CheckStatus.Unknown, nameof(value), "Check status 'Unknown' is not valid for partial success.");
+
+                _partialSuccessStatus = value;
+            }
+        }
+    }
+}
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs
index 7ea31b42a..d8ef80dc4 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs
@@ -2,7 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Collections.Generic;
-using System.Linq;
 
 namespace Microsoft.Extensions.HealthChecks
 {
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs
index 6e16bed61..1d2934e0e 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs
@@ -3,52 +3,117 @@
 
 using System;
 using System.Collections.Generic;
-using System.Text;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
 
 namespace Microsoft.Extensions.HealthChecks
 {
     public class HealthCheckService : IHealthCheckService
     {
-        public IReadOnlyDictionary<string, IHealthCheck> _checks;
+        private readonly HealthCheckBuilder _builder;
+        private readonly IReadOnlyList<HealthCheckGroup> _groups;
+        private readonly HealthCheckGroup _root;
+        private readonly IServiceProvider _serviceProvider;
+        private readonly IServiceScopeFactory _serviceScopeFactory;
 
-        private ILogger<HealthCheckService> _logger;
-
-        public HealthCheckService(HealthCheckBuilder builder, ILogger<HealthCheckService> logger)
+        public HealthCheckService(HealthCheckBuilder builder, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
         {
-            _checks = builder.Checks;
-            _logger = logger;
+            _builder = builder;
+            _groups = GetGroups().Where(group => group.GroupName != string.Empty).ToList();
+            _root = GetGroup(string.Empty);
+            _serviceProvider = serviceProvider;
+            _serviceScopeFactory = serviceScopeFactory;
         }
 
-        public async Task<CompositeHealthCheckResult> CheckHealthAsync(CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken)
+        public async Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken))
         {
-            var logMessage = new StringBuilder();
-            var result = new CompositeHealthCheckResult(partiallyHealthyStatus);
-
-            foreach (var check in _checks)
+            using (var scope = GetServiceScope())
             {
-                try
+                var scopeServiceProvider = scope.ServiceProvider;
+                var groupTasks = _groups.Select(group => new { Group = group, Task = RunGroupAsync(scopeServiceProvider, group, cancellationToken) }).ToList();
+                var result = await RunGroupAsync(scopeServiceProvider, _root, cancellationToken).ConfigureAwait(false);
+
+                await Task.WhenAll(groupTasks.Select(x => x.Task));
+
+                foreach (var groupTask in groupTasks)
                 {
-                    var healthCheckResult = await check.Value.CheckAsync().ConfigureAwait(false);
-                    logMessage.AppendLine($"HealthCheck: {check.Key} : {healthCheckResult.CheckStatus}");
-                    result.Add(check.Key, healthCheckResult);
-                }
-                catch (Exception ex)
-                {
-                    logMessage.AppendLine($"HealthCheck: {check.Key} : Exception {ex.GetType().FullName} thrown");
-                    result.Add(check.Key, CheckStatus.Unhealthy, $"Exception during check: {ex.GetType().FullName}");
+                    result.Add($"Group({groupTask.Group.GroupName})", groupTask.Task.Result);
                 }
+
+                return result;
+            }
+        }
+
+        public IReadOnlyList<CachedHealthCheck> GetAllChecks()
+            => _builder.ChecksByName.Values.ToList().AsReadOnly();
+
+        public CachedHealthCheck GetCheck(string checkName)
+            => _builder.ChecksByName[checkName];
+
+        public HealthCheckGroup GetGroup(string groupName)
+            => _builder.Groups[groupName];
+
+        public IReadOnlyList<HealthCheckGroup> GetGroups()
+            => _builder.Groups.Values.ToList().AsReadOnly();
+
+        private IServiceScope GetServiceScope()
+            => _serviceScopeFactory == null ? new UnscopedServiceProvider(_serviceProvider) : _serviceScopeFactory.CreateScope();
+
+        public async ValueTask<IHealthCheckResult> RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            using (var scope = GetServiceScope())
+            {
+                return await RunCheckAsync(scope.ServiceProvider, healthCheck, cancellationToken).ConfigureAwait(false);
+            }
+        }
+
+        /// <summary>
+        /// Uses the provided service provider and executes the provided check.
+        /// </summary>
+        public ValueTask<IHealthCheckResult> RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            Guard.ArgumentNotNull(nameof(serviceProvider), serviceProvider);
+            Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
+
+            return healthCheck.RunAsync(serviceProvider, cancellationToken);
+        }
+
+        /// <summary>
+        /// Creates a new resolution scope from the default service provider and executes the checks in the given group.
+        /// </summary>
+        public async Task<CompositeHealthCheckResult> RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            using (var scope = GetServiceScope())
+                return await RunGroupAsync(scope.ServiceProvider, group, cancellationToken).ConfigureAwait(false);
+        }
+
+        /// <summary>
+        /// Uses the provided service provider and executes the checks in the given group.
+        /// </summary>
+        public async Task<CompositeHealthCheckResult> RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            var result = new CompositeHealthCheckResult(group.PartiallyHealthyStatus);
+            var checkTasks = group.Checks.Select(check => new { Check = check, Task = check.RunAsync(serviceProvider, cancellationToken).AsTask() }).ToList();
+            await Task.WhenAll(checkTasks.Select(checkTask => checkTask.Task));
+
+            foreach (var checkTask in checkTasks)
+            {
+                result.Add(checkTask.Check.Name, checkTask.Task.Result);
             }
 
-            if (logMessage.Length == 0)
-                logMessage.AppendLine("HealthCheck: No checks have been registered");
-
-            _logger.Log((result.CheckStatus == CheckStatus.Healthy ? LogLevel.Information : LogLevel.Error), 0, logMessage.ToString(), null, MessageFormatter);
             return result;
         }
 
-        private static string MessageFormatter(string state, Exception error) => state;
+        private class UnscopedServiceProvider : IServiceScope
+        {
+            public UnscopedServiceProvider(IServiceProvider serviceProvider)
+                => ServiceProvider = serviceProvider;
+
+            public IServiceProvider ServiceProvider { get; }
+
+            public void Dispose() { }
+        }
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs
index cdd763d9f..678731737 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs
@@ -2,20 +2,28 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Linq;
 using Microsoft.Extensions.HealthChecks;
 
 namespace Microsoft.Extensions.DependencyInjection
 {
     public static class HealthCheckServiceCollectionExtensions
     {
-        public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checkupAction)
+        private static readonly Type HealthCheckServiceInterface = typeof(IHealthCheckService);
+
+        public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checks)
         {
-            var checkupBuilder = new HealthCheckBuilder();
+            Guard.OperationValid(!services.Any(descriptor => descriptor.ServiceType == HealthCheckServiceInterface), "AddHealthChecks may only be called once.");
 
-            checkupAction.Invoke(checkupBuilder);
+            var builder = new HealthCheckBuilder();
 
-            services.AddSingleton(checkupBuilder);
-            services.AddSingleton<IHealthCheckService, HealthCheckService>();
+            services.AddSingleton<IHealthCheckService, HealthCheckService>(serviceProvider =>
+            {
+                var serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
+                return new HealthCheckService(builder, serviceProvider, serviceScopeFactory);
+            });
+
+            checks(builder);
             return services;
         }
     }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceExtensions.cs
deleted file mode 100644
index 98ab54b70..000000000
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceExtensions.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.Extensions.HealthChecks
-{
-    public static class HealthCheckServiceExtensions
-    {
-        public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service)
-        {
-            Guard.ArgumentNotNull(nameof(service), service);
-
-            return service.CheckHealthAsync(CheckStatus.Unhealthy, CancellationToken.None);
-        }
-
-        public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CheckStatus partiallyHealthyStatus)
-        {
-            Guard.ArgumentNotNull(nameof(service), service);
-
-            return service.CheckHealthAsync(partiallyHealthyStatus, CancellationToken.None);
-        }
-
-        public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CancellationToken cancellationToken)
-        {
-            Guard.ArgumentNotNull(nameof(service), service);
-
-            return service.CheckHealthAsync(CheckStatus.Unhealthy, cancellationToken);
-        }
-        public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken)
-        {
-            Guard.ArgumentNotNull(nameof(service), service);
-
-            return service.CheckHealthAsync(partiallyHealthyStatus, cancellationToken);
-        }
-    }
-}
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs
index b5b95405f..e4aa45d28 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs
@@ -1,7 +1,6 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
-using System;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -9,8 +8,6 @@ namespace Microsoft.Extensions.HealthChecks
 {
     public interface IHealthCheck
     {
-        TimeSpan CacheDuration { get; }
-
-        ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken);
+        ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken));
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs
index 60e917264..17a49cb00 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs
@@ -1,6 +1,8 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -8,6 +10,49 @@ namespace Microsoft.Extensions.HealthChecks
 {
     public interface IHealthCheckService
     {
-        Task<CompositeHealthCheckResult> CheckHealthAsync(CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken);
+        /// <summary>
+        /// Runs all registered health checks.
+        /// </summary>
+        Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken));
+
+        /// <summary>
+        /// Gets all registered health checks as a flat list.
+        /// </summary>
+        IReadOnlyList<CachedHealthCheck> GetAllChecks();
+
+        /// <summary>
+        /// Gets a health check by name.
+        /// </summary>
+        CachedHealthCheck GetCheck(string checkName);
+
+        /// <summary>
+        /// Gets all health checks in a group.
+        /// </summary>
+        HealthCheckGroup GetGroup(string groupName);
+
+        /// <summary>
+        /// Gets all the health check groups.
+        /// </summary>
+        IReadOnlyList<HealthCheckGroup> GetGroups();
+
+        /// <summary>
+        /// Creates a new resolution scope from the default service provider and executes the provided check.
+        /// </summary>
+        ValueTask<IHealthCheckResult> RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken));
+
+        /// <summary>
+        /// Uses the provided service provider and executes the provided check.
+        /// </summary>
+        ValueTask<IHealthCheckResult> RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken));
+
+        /// <summary>
+        /// Creates a new resolution scope from the default service provider and executes the checks in the given group.
+        /// </summary>
+        Task<CompositeHealthCheckResult> RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken));
+
+        /// <summary>
+        /// Uses the provided service provider and executes the checks in the given group.
+        /// </summary>
+        Task<CompositeHealthCheckResult> RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken));
     }
 }
diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs
index ad021b149..56800d334 100644
--- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs
+++ b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs
@@ -3,7 +3,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Net;
 using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Threading.Tasks;
@@ -13,55 +12,33 @@ namespace Microsoft.Extensions.HealthChecks.Internal
     public class UrlChecker
     {
         private readonly Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> _checkFunc;
-        private readonly string[] _urls;
+        private readonly string _url;
 
-        // REVIEW: Cache timeout here?
-        public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, params string[] urls)
+        public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, string url)
         {
             Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
-            Guard.ArgumentNotNullOrEmpty(nameof(urls), urls);
+            Guard.ArgumentNotNullOrEmpty(nameof(url), url);
 
             _checkFunc = checkFunc;
-            _urls = urls;
+            _url = url;
         }
 
         public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning;
 
-        public Task<IHealthCheckResult> CheckAsync()
-            => _urls.Length == 1 ? CheckSingleAsync() : CheckMultiAsync();
-
-        public async Task<IHealthCheckResult> CheckSingleAsync()
+        public async Task<IHealthCheckResult> CheckAsync()
         {
-            var httpClient = CreateHttpClient();
-            var result = default(IHealthCheckResult);
-            await CheckUrlAsync(httpClient, _urls[0], (_, checkResult) => result = checkResult).ConfigureAwait(false);
-            return result;
-        }
-
-        public async Task<IHealthCheckResult> CheckMultiAsync()
-        {
-            var composite = new CompositeHealthCheckResult(PartiallyHealthyStatus);
-            var httpClient = CreateHttpClient();
-
-            // REVIEW: Should these be done in parallel?
-            foreach (var url in _urls)
-                await CheckUrlAsync(httpClient, url, (name, checkResult) => composite.Add(name, checkResult)).ConfigureAwait(false);
-
-            return composite;
-        }
-
-        private async Task CheckUrlAsync(HttpClient httpClient, string url, Action<string, IHealthCheckResult> adder)
-        {
-            var name = $"UrlCheck({url})";
-            try
+            using (var httpClient = CreateHttpClient())
             {
-                var response = await httpClient.GetAsync(url).ConfigureAwait(false);
-                var result = await _checkFunc(response);
-                adder(name, result);
-            }
-            catch (Exception ex)
-            {
-                adder(name, HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}"));
+                try
+                {
+                    var response = await httpClient.GetAsync(_url).ConfigureAwait(false);
+                    return await _checkFunc(response);
+                }
+                catch (Exception ex)
+                {
+                    var data = new Dictionary<string, object> { { "url", _url } };
+                    return HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}", data);
+                }
             }
         }
 
@@ -74,8 +51,7 @@ namespace Microsoft.Extensions.HealthChecks.Internal
 
         public static async ValueTask<IHealthCheckResult> DefaultUrlCheck(HttpResponseMessage response)
         {
-            // REVIEW: Should this be an explicit 200 check, or just an "is success" check?
-            var status = response.StatusCode == HttpStatusCode.OK ? CheckStatus.Healthy : CheckStatus.Unhealthy;
+            var status = response.IsSuccessStatusCode ? CheckStatus.Healthy : CheckStatus.Unhealthy;
             var data = new Dictionary<string, object>
             {
                 { "url", response.RequestMessage.RequestUri.ToString() },
@@ -83,7 +59,7 @@ namespace Microsoft.Extensions.HealthChecks.Internal
                 { "reason", response.ReasonPhrase },
                 { "body", await response.Content?.ReadAsStringAsync() }
             };
-            return HealthCheckResult.FromStatus(status, $"UrlCheck({response.RequestMessage.RequestUri}): status code {response.StatusCode} ({(int)response.StatusCode})", data);
+            return HealthCheckResult.FromStatus(status, $"status code {response.StatusCode} ({(int)response.StatusCode})", data);
         }
 
         protected virtual HttpClient GetHttpClient()
diff --git a/src/BuildingBlocks/HealthChecks/src/common/Guard.cs b/src/BuildingBlocks/HealthChecks/src/common/Guard.cs
index 8ed91054e..9f394be51 100644
--- a/src/BuildingBlocks/HealthChecks/src/common/Guard.cs
+++ b/src/BuildingBlocks/HealthChecks/src/common/Guard.cs
@@ -9,37 +9,49 @@ static class Guard
     public static void ArgumentNotNull(string argumentName, object value)
     {
         if (value == null)
+        {
             throw new ArgumentNullException(argumentName);
+        }
     }
 
-    public static void ArgumentNotNullOrEmpty<T>(string argumentName, string value)
+    public static void ArgumentNotNullOrEmpty(string argumentName, string value)
     {
         if (value == null)
+        {
             throw new ArgumentNullException(argumentName);
+        }
         if (string.IsNullOrEmpty(value))
-            throw new ArgumentException("Value cannot be an empty string", argumentName);
+        {
+            throw new ArgumentException("Value cannot be an empty string.", argumentName);
+        }
     }
 
     // Use IReadOnlyCollection<T> instead of IEnumerable<T> to discourage double enumeration
     public static void ArgumentNotNullOrEmpty<T>(string argumentName, IReadOnlyCollection<T> items)
     {
         if (items == null)
+        {
             throw new ArgumentNullException(argumentName);
+        }
         if (items.Count == 0)
-            throw new ArgumentException("Collection must contain at least one item", argumentName);
-    }
-
-    public static void ArgumentNotNullOrWhitespace(string argumentName, string value)
-    {
-        if (value == null)
-            throw new ArgumentNullException(argumentName);
-        if (string.IsNullOrWhiteSpace(value))
-            throw new ArgumentException("Value must contain a non-whitespace value", argumentName);
+        {
+            throw new ArgumentException("Collection must contain at least one item.", argumentName);
+        }
     }
 
     public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage)
     {
         if (!valid)
+        {
             throw new ArgumentException(exceptionMessage, argumentName);
+        }
+    }
+
+    public static void OperationValid(bool valid, string exceptionMessage)
+    {
+        if (!valid)
+        {
+            throw new InvalidOperationException(exceptionMessage);
+        }
     }
 }
diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj
index ce20513cc..b3ba97b10 100644
--- a/src/Services/Basket/Basket.API/Basket.API.csproj
+++ b/src/Services/Basket/Basket.API/Basket.API.csproj
@@ -43,7 +43,6 @@
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
   </ItemGroup>
 
diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
index 5acd0bbdc..ab7989973 100644
--- a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
+++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
@@ -4,11 +4,7 @@ using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.Filters;
 using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Net;
-using System.Threading.Tasks;
 
 namespace Basket.API.Infrastructure.Filters
 {
@@ -43,12 +39,12 @@ namespace Basket.API.Infrastructure.Filters
             {
                 var json = new JsonErrorResponse
                 {
-                    Messages = new[] { "An error ocurr.Try it again." }
+                    Messages = new[] { "An error occurred. Try it again." }
                 };
 
                 if (env.IsDevelopment())
                 {
-                    json.DeveloperMeesage = context.Exception;
+                    json.DeveloperMessage = context.Exception;
                 }
 
                 context.Result = new InternalServerErrorObjectResult(json);
@@ -61,7 +57,7 @@ namespace Basket.API.Infrastructure.Filters
         {
             public string[] Messages { get; set; }
 
-            public object DeveloperMeesage { get; set; }
+            public object DeveloperMessage { get; set; }
         }
     }
 }
diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs
index 60fc46de2..855312a65 100644
--- a/src/Services/Basket/Basket.API/Startup.cs
+++ b/src/Services/Basket/Basket.API/Startup.cs
@@ -3,6 +3,7 @@ using Basket.API.IntegrationEvents.EventHandling;
 using Basket.API.IntegrationEvents.Events;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
 using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
 using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
 using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
@@ -19,6 +20,7 @@ using StackExchange.Redis;
 using System.Linq;
 using System.Net;
 using System.Threading.Tasks;
+using System;
 
 namespace Microsoft.eShopOnContainers.Services.Basket.API
 {
@@ -68,20 +70,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
             });
 
 
-            services.AddSingleton<IRabbitMQPersisterConnection>(sp =>
+            services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
             {
                 var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
-                var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersisterConnection>>();
+                var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
                 var factory = new ConnectionFactory()
                 {
                     HostName = settings.EventBusConnection
                 };
 
-                return new DefaultRabbitMQPersisterConnection(factory, logger);
+                return new DefaultRabbitMQPersistentConnection(factory, logger);
             });
 
-            services.AddSingleton<IEventBus, EventBusRabbitMQ>();
-
             services.AddSwaggerGen();
 
             services.ConfigureSwaggerGen(options =>
@@ -108,9 +108,16 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
             });
 
             services.AddTransient<IBasketRepository, RedisBasketRepository>();
-            services.AddTransient<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>, ProductPriceChangedIntegrationEventHandler>();
-            services.AddTransient<IIntegrationEventHandler<OrderStartedIntegrationEvent>, OrderStartedIntegrationEventHandler>();
+            RegisterServiceBus(services);
+        }
 
+        private void RegisterServiceBus(IServiceCollection services)
+        {
+            services.AddSingleton<IEventBus, EventBusRabbitMQ>();
+            services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
+
+            services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
+            services.AddTransient<OrderStartedIntegrationEventHandler>();
 
         }
 
@@ -155,11 +162,13 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
             var orderStartedHandler = app.ApplicationServices
                 .GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();
 
-            var eventBus = app.ApplicationServices
-                .GetRequiredService<IEventBus>();
+            var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
 
-            eventBus.Subscribe<ProductPriceChangedIntegrationEvent>(catalogPriceHandler);
-            eventBus.Subscribe<OrderStartedIntegrationEvent>(orderStartedHandler);
+            eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>
+                (() => app.ApplicationServices.GetRequiredService<ProductPriceChangedIntegrationEventHandler>());
+
+            eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>
+                (() => app.ApplicationServices.GetRequiredService<OrderStartedIntegrationEventHandler>());
         }
     }
 }
diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj
index d805e06e0..4306d6922 100644
--- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj
+++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj
@@ -59,7 +59,7 @@
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
+    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
   </ItemGroup>
diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs
index c13ac2d1b..9eb195674 100644
--- a/src/Services/Catalog/Catalog.API/Startup.cs
+++ b/src/Services/Catalog/Catalog.API/Startup.cs
@@ -6,6 +6,7 @@
     using Microsoft.AspNetCore.Hosting;
     using Microsoft.EntityFrameworkCore;
     using Microsoft.EntityFrameworkCore.Infrastructure;
+    using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
     using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
     using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
@@ -103,18 +104,19 @@
 
             services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>();
 
-            services.AddSingleton<IRabbitMQPersisterConnection>(sp =>
+            services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
             {
                 var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value;
-                var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersisterConnection>>();
+                var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
                 var factory = new ConnectionFactory()
                 {
                     HostName = settings.EventBusConnection
                 };
 
-                return new DefaultRabbitMQPersisterConnection(factory, logger);
+                return new DefaultRabbitMQPersistentConnection(factory, logger);
             });
 
+            services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
             services.AddSingleton<IEventBus, EventBusRabbitMQ>();
         }
 
diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj
index 043595232..dda24079e 100644
--- a/src/Services/Identity/Identity.API/Identity.API.csproj
+++ b/src/Services/Identity/Identity.API/Identity.API.csproj
@@ -63,7 +63,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
+    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
   </ItemGroup>
 
diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs
index 1485a536f..950c4bdc5 100644
--- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs
+++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs
@@ -61,20 +61,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
         [DataMember]
         public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
 
-        public void AddOrderItem(OrderItemDTO item)
-        {
-            _orderItems.Add(item);
-        }
-
         public CreateOrderCommand()
         {
             _orderItems = new List<OrderItemDTO>();
         }
 
-        public CreateOrderCommand(string city, string street, string state, string country, string zipcode,
+        public CreateOrderCommand(List<OrderItemDTO> orderItems, string city, string street, string state, string country, string zipcode,
             string cardNumber, string cardHolderName, DateTime cardExpiration,
             string cardSecurityNumber, int cardTypeId, int paymentId, int buyerId) : this()
         {
+            _orderItems = orderItems;
             City = city;
             Street = street;
             State = state;
diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj
index 6eed3a13c..975645ce4 100644
--- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj
+++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj
@@ -27,7 +27,7 @@
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
+    <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
     <ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
     <ProjectReference Include="..\Ordering.Domain\Ordering.Domain.csproj" />
     <ProjectReference Include="..\Ordering.Infrastructure\Ordering.Infrastructure.csproj" />
diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs
index 0d6e222b6..58d8f1cbe 100644
--- a/src/Services/Ordering/Ordering.API/Startup.cs
+++ b/src/Services/Ordering/Ordering.API/Startup.cs
@@ -13,6 +13,7 @@
     using Microsoft.AspNetCore.Builder;
     using Microsoft.AspNetCore.Hosting;
     using Microsoft.EntityFrameworkCore;
+    using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
     using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
     using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
@@ -107,18 +108,19 @@
             var serviceProvider = services.BuildServiceProvider();
             services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
 
-            services.AddSingleton<IRabbitMQPersisterConnection>(sp =>
+            services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
             {
-                var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersisterConnection>>();
+                var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
 
                 var factory = new ConnectionFactory()
                 {
                     HostName = Configuration["EventBusConnection"]
                 };
 
-                return new DefaultRabbitMQPersisterConnection(factory, logger);
+                return new DefaultRabbitMQPersistentConnection(factory, logger);
             });
 
+            services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
             services.AddSingleton<IEventBus, EventBusRabbitMQ>();
 
             services.AddOptions();
diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs
index 5b95ee23c..18d66534b 100644
--- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs
+++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs
@@ -252,7 +252,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
 
 
             // After executing this line all the changes (from the Command Handler and Domain Event Handlers) 
-            // performed thought the DbContext will be commited
+            // performed throught the DbContext will be commited
             var result = await base.SaveChangesAsync();
 
             return true;
diff --git a/src/Web/WebMVC/WebMVC.csproj b/src/Web/WebMVC/WebMVC.csproj
index 20a8baca0..3f0f6b84b 100644
--- a/src/Web/WebMVC/WebMVC.csproj
+++ b/src/Web/WebMVC/WebMVC.csproj
@@ -59,7 +59,6 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
     <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
     <ProjectReference Include="..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
   </ItemGroup>
diff --git a/src/Web/WebMVC/wwwroot/css/site.min.css b/src/Web/WebMVC/wwwroot/css/site.min.css
index 4d03fa783..f5fc90999 100644
--- a/src/Web/WebMVC/wwwroot/css/site.min.css
+++ b/src/Web/WebMVC/wwwroot/css/site.min.css
@@ -1 +1 @@
-.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px}.esh-app-footer-text{color:#83d01b;line-height:50px;text-align:right;width:100%}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}html,body{font-family:Montserrat,sans-serif;font-size:16px;font-weight:400;z-index:10}*,*::after,*::before{box-sizing:border-box}.preloading{color:#00a69c;display:block;font-size:1.5rem;left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}select::-ms-expand{display:none}@media screen and (min-width:992px){.form-input{max-width:360px;width:360px}}.form-input{border-radius:0;height:45px;padding:10px}.form-input-small{max-width:100px !important}.form-input-medium{width:150px !important}.alert{padding-left:0}.alert-danger{background-color:transparent;border:0;color:#fb0d0d;font-size:12px}a,a:active,a:hover,a:visited{color:#000;text-decoration:none;transition:color .35s}a:hover,a:active{color:#75b918;transition:color .35s}.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-basket-items--border:last-of-type{border-color:transparent}.esh-basket-item{font-size:1rem;font-weight:300}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{border:none;border-radius:0;background-color:#83d01b;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s}.esh-basket-margin12{margin-left:12px}.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s}.esh-catalog-hero{background-image:url("../images/main_banner.png");background-size:cover;height:260px;width:100%}.esh-catalog-title{position:relative;top:74.28571px}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem;min-width:140px;-webkit-appearance:none}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-top:.65rem;margin-left:.5rem;position:absolute;text-transform:uppercase;z-index:1}.esh-catalog-label::after{background-image:url("../images/arrow-down.png");height:7px;content:'';position:absolute;right:1.5rem;top:2.5rem;width:10px;z-index:1}.esh-catalog-send{background-color:#83d01b;color:#fff;cursor:pointer;font-size:1rem;transform:translateY(.5rem);padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{text-align:center;margin-bottom:1.5rem;width:33%;display:inline-block;float:none !important}@media screen and (max-width:1024px){.esh-catalog-item{width:50%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:none;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{text-align:center;font-weight:900;font-size:28px}.esh-catalog-price::before{content:'$'}.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s}.esh-orders_detail{min-height:80vh}.esh-orders_detail-section{padding:1rem 0}.esh-orders_detail-section--right{text-align:right}.esh-orders_detail-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_detail-title{text-transform:uppercase}.esh-orders_detail-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_detail-items--border:last-of-type{border-color:transparent}.esh-orders_detail-item{font-size:1rem;font-weight:300}.esh-orders_detail-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_detail-item--middle{line-height:1rem}}.esh-orders_detail-item--mark{color:#83d01b}.esh-orders_detail-image{height:8rem}.esh-orders_new{min-height:80vh}.esh-orders_new-header{background-color:#00a69c;height:4rem}.esh-orders_new-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders_new-back:hover{color:#fff;transition:color .35s}.esh-orders_new-section{padding:1rem 0}.esh-orders_new-section--right{text-align:right}.esh-orders_new-placeOrder{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-orders_new-placeOrder:hover{background-color:#4a760f;transition:all .35s}.esh-orders_new-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_new-title{font-size:1.25rem;text-transform:uppercase}.esh-orders_new-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_new-items--border:last-of-type{border-color:transparent}.esh-orders_new-item{font-size:1rem;font-weight:300}.esh-orders_new-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_new-item--middle{line-height:1rem}}.esh-orders_new-item--mark{color:#83d01b}.esh-orders_new-image{height:8rem}.esh-header{background-color:#00a69c;height:4rem}.esh-header-back{color:rgba(255,255,255,.5) !important;line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-header-back:hover{color:#fff !important;transition:color .35s}.esh-identity{line-height:3rem;position:relative;text-align:right}.esh-identity-section{display:inline-block;width:100%}.esh-identity-name{display:inline-block}.esh-identity-name--upper{text-transform:uppercase}@media screen and (max-width:768px){.esh-identity-name{font-size:.85rem}}.esh-identity-image{display:inline-block}.esh-identity-drop{background:#fff;height:0;min-width:14rem;right:0;overflow:hidden;padding:.5rem;position:absolute;top:2.5rem;transition:height .35s}.esh-identity:hover .esh-identity-drop{border:1px solid #eee;height:7rem;transition:height .35s}.esh-identity-item{cursor:pointer;display:block;transition:color .35s}.esh-identity-item:hover{color:#75b918;transition:color .35s}.esh-pager-wrapper{padding-top:1rem;text-align:center}.esh-pager-item{margin:0 5vw}.esh-pager-item--navigable{display:inline-block;cursor:pointer}.esh-pager-item--navigable.is-disabled{opacity:0;pointer-events:none}.esh-pager-item--navigable:hover{color:#83d01b}@media screen and (max-width:1280px){.esh-pager-item{font-size:.85rem}}@media screen and (max-width:1024px){.esh-pager-item{margin:0 4vw}}
\ No newline at end of file
+.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px}.esh-app-footer-text{color:#83d01b;line-height:50px;text-align:right;width:100%}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}html,body{font-family:Montserrat,sans-serif;font-size:16px;font-weight:400;z-index:10}*,*::after,*::before{box-sizing:border-box}.preloading{color:#00a69c;display:block;font-size:1.5rem;left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}select::-ms-expand{display:none}@media screen and (min-width:992px){.form-input{max-width:360px;width:360px}}.form-input{border-radius:0;height:45px;padding:10px}.form-input-small{max-width:100px !important}.form-input-medium{width:150px !important}.alert{padding-left:0}.alert-danger{background-color:transparent;border:0;color:#fb0d0d;font-size:12px}a,a:active,a:hover,a:visited{color:#000;text-decoration:none;transition:color .35s}a:hover,a:active{color:#75b918;transition:color .35s}.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-basket-items--border:last-of-type{border-color:transparent}.esh-basket-item{font-size:1rem;font-weight:300}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{border:none;border-radius:0;background-color:#83d01b;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s}.esh-basket-margin12{margin-left:12px}.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s}.esh-catalog-hero{background-image:url("../images/main_banner.png");background-size:cover;height:260px;width:100%}.esh-catalog-title{position:relative;top:74.28571px}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem;min-width:140px;-webkit-appearance:none}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-top:.65rem;margin-left:.5rem;position:absolute;text-transform:uppercase;z-index:1}.esh-catalog-label::after{background-image:url("../images/arrow-down.png");height:7px;content:'';position:absolute;right:1.5rem;top:2.5rem;width:10px;z-index:1}.esh-catalog-send{background-color:#83d01b;color:#fff;cursor:pointer;font-size:1rem;transform:translateY(.5rem);padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{text-align:center;margin-bottom:1.5rem;width:33%;display:inline-block;float:none !important}@media screen and (max-width:1024px){.esh-catalog-item{width:50%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:none;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{text-align:center;font-weight:900;font-size:28px}.esh-catalog-price::before{content:'$'}.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s}.esh-orders_new{min-height:80vh}.esh-orders_new-header{background-color:#00a69c;height:4rem}.esh-orders_new-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders_new-back:hover{color:#fff;transition:color .35s}.esh-orders_new-section{padding:1rem 0}.esh-orders_new-section--right{text-align:right}.esh-orders_new-placeOrder{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-orders_new-placeOrder:hover{background-color:#4a760f;transition:all .35s}.esh-orders_new-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_new-title{font-size:1.25rem;text-transform:uppercase}.esh-orders_new-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_new-items--border:last-of-type{border-color:transparent}.esh-orders_new-item{font-size:1rem;font-weight:300}.esh-orders_new-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_new-item--middle{line-height:1rem}}.esh-orders_new-item--mark{color:#83d01b}.esh-orders_new-image{height:8rem}.esh-orders_detail{min-height:80vh}.esh-orders_detail-section{padding:1rem 0}.esh-orders_detail-section--right{text-align:right}.esh-orders_detail-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_detail-title{text-transform:uppercase}.esh-orders_detail-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_detail-items--border:last-of-type{border-color:transparent}.esh-orders_detail-item{font-size:1rem;font-weight:300}.esh-orders_detail-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_detail-item--middle{line-height:1rem}}.esh-orders_detail-item--mark{color:#83d01b}.esh-orders_detail-image{height:8rem}.esh-identity{line-height:3rem;position:relative;text-align:right}.esh-identity-section{display:inline-block;width:100%}.esh-identity-name{display:inline-block}.esh-identity-name--upper{text-transform:uppercase}@media screen and (max-width:768px){.esh-identity-name{font-size:.85rem}}.esh-identity-image{display:inline-block}.esh-identity-drop{background:#fff;height:0;min-width:14rem;right:0;overflow:hidden;padding:.5rem;position:absolute;top:2.5rem;transition:height .35s}.esh-identity:hover .esh-identity-drop{border:1px solid #eee;height:7rem;transition:height .35s}.esh-identity-item{cursor:pointer;display:block;transition:color .35s}.esh-identity-item:hover{color:#75b918;transition:color .35s}.esh-header{background-color:#00a69c;height:4rem}.esh-header-back{color:rgba(255,255,255,.5) !important;line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-header-back:hover{color:#fff !important;transition:color .35s}.esh-pager-wrapper{padding-top:1rem;text-align:center}.esh-pager-item{margin:0 5vw}.esh-pager-item--navigable{display:inline-block;cursor:pointer}.esh-pager-item--navigable.is-disabled{opacity:0;pointer-events:none}.esh-pager-item--navigable:hover{color:#83d01b}@media screen and (max-width:1280px){.esh-pager-item{font-size:.85rem}}@media screen and (max-width:1024px){.esh-pager-item{margin:0 4vw}}
\ No newline at end of file
diff --git a/src/Web/WebSPA/.angular-cli.json b/src/Web/WebSPA/.angular-cli.json
new file mode 100644
index 000000000..131f2897c
--- /dev/null
+++ b/src/Web/WebSPA/.angular-cli.json
@@ -0,0 +1,58 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "project": {
+    "name": "WebSPA"
+  },
+  "apps": [
+    {
+      "root": "Client",
+      "outDir": "wwwroot",
+      "assets": [
+        "assets",
+        "favicon.ico"
+      ],
+      "index": "index.html",
+      "main": "main.ts",
+      "polyfills": "polyfills.ts",
+      "test": "test.ts",
+      "tsconfig": "tsconfig.app.json",
+      "testTsconfig": "tsconfig.spec.json",
+      "prefix": "app",
+      "styles": [
+        "globals.scss",
+        "../node_modules/bootstrap/scss/bootstrap.scss"
+      ],
+      "scripts": [],
+      "environmentSource": "environments/environment.ts",
+      "environments": {
+        "dev": "environments/environment.ts",
+        "prod": "environments/environment.prod.ts"
+      }
+    }
+  ],
+  "e2e": {
+    "protractor": {
+      "config": "./protractor.conf.js"
+    }
+  },
+  "lint": [
+    {
+      "project": "Client/tsconfig.app.json"
+    },
+    {
+      "project": "Client/tsconfig.spec.json"
+    },
+    {
+      "project": "e2e/tsconfig.e2e.json"
+    }
+  ],
+  "test": {
+    "karma": {
+      "config": "./karma.conf.js"
+    }
+  },
+  "defaults": {
+    "styleExt": "scss",
+    "component": {}
+  }
+}
diff --git a/src/Web/WebSPA/.gitignore b/src/Web/WebSPA/.gitignore
index 280332f79..a4cb1babf 100644
--- a/src/Web/WebSPA/.gitignore
+++ b/src/Web/WebSPA/.gitignore
@@ -177,10 +177,7 @@ ClientBin/
 *.publishsettings
 node_modules/
 bower_components/
-**/wwwroot/tmp/
-**/wwwroot/*.bundle.map
-**/wwwroot/*.js
-/wwwroot/dist/
+wwwroot/
 
 
 orleans.codegen.cs
diff --git a/src/Web/WebSPA/Client/assets/.gitkeep b/src/Web/WebSPA/Client/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/Web/WebSPA/Client/images/arrow-down.png b/src/Web/WebSPA/Client/assets/images/arrow-down.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/arrow-down.png
rename to src/Web/WebSPA/Client/assets/images/arrow-down.png
diff --git a/src/Web/WebSPA/Client/images/arrow-right.svg b/src/Web/WebSPA/Client/assets/images/arrow-right.svg
similarity index 100%
rename from src/Web/WebSPA/Client/images/arrow-right.svg
rename to src/Web/WebSPA/Client/assets/images/arrow-right.svg
diff --git a/src/Web/WebSPA/Client/images/brand.png b/src/Web/WebSPA/Client/assets/images/brand.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/brand.png
rename to src/Web/WebSPA/Client/assets/images/brand.png
diff --git a/src/Web/WebSPA/Client/images/brand_dark.png b/src/Web/WebSPA/Client/assets/images/brand_dark.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/brand_dark.png
rename to src/Web/WebSPA/Client/assets/images/brand_dark.png
diff --git a/src/Web/WebSPA/Client/images/cart.png b/src/Web/WebSPA/Client/assets/images/cart.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/cart.png
rename to src/Web/WebSPA/Client/assets/images/cart.png
diff --git a/src/Web/WebSPA/Client/images/logout.png b/src/Web/WebSPA/Client/assets/images/logout.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/logout.png
rename to src/Web/WebSPA/Client/assets/images/logout.png
diff --git a/src/Web/WebSPA/Client/images/main_banner.png b/src/Web/WebSPA/Client/assets/images/main_banner.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/main_banner.png
rename to src/Web/WebSPA/Client/assets/images/main_banner.png
diff --git a/src/Web/WebSPA/Client/images/main_banner_text.png b/src/Web/WebSPA/Client/assets/images/main_banner_text.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/main_banner_text.png
rename to src/Web/WebSPA/Client/assets/images/main_banner_text.png
diff --git a/src/Web/WebSPA/Client/images/my_orders.png b/src/Web/WebSPA/Client/assets/images/my_orders.png
similarity index 100%
rename from src/Web/WebSPA/Client/images/my_orders.png
rename to src/Web/WebSPA/Client/assets/images/my_orders.png
diff --git a/src/Web/WebSPA/Client/custom-typings.d.ts b/src/Web/WebSPA/Client/custom-typings.d.ts
deleted file mode 100644
index 8e46a4e30..000000000
--- a/src/Web/WebSPA/Client/custom-typings.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// Extra variables that live on Global that will be replaced by webpack DefinePlugin
-// declare var process: any;
diff --git a/src/Web/WebSPA/Client/environments/environment.prod.ts b/src/Web/WebSPA/Client/environments/environment.prod.ts
new file mode 100644
index 000000000..3612073bc
--- /dev/null
+++ b/src/Web/WebSPA/Client/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+  production: true
+};
diff --git a/src/Web/WebSPA/Client/environments/environment.ts b/src/Web/WebSPA/Client/environments/environment.ts
new file mode 100644
index 000000000..b7f639aec
--- /dev/null
+++ b/src/Web/WebSPA/Client/environments/environment.ts
@@ -0,0 +1,8 @@
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+  production: false
+};
diff --git a/src/Web/WebSPA/wwwroot/favicon.ico b/src/Web/WebSPA/Client/favicon.ico
similarity index 100%
rename from src/Web/WebSPA/wwwroot/favicon.ico
rename to src/Web/WebSPA/Client/favicon.ico
diff --git a/src/Web/WebSPA/Client/globals.scss b/src/Web/WebSPA/Client/globals.scss
index fde578e3d..e69b955dc 100644
--- a/src/Web/WebSPA/Client/globals.scss
+++ b/src/Web/WebSPA/Client/globals.scss
@@ -1,3 +1,4 @@
+/* You can add global styles to this file, and also import other style files */
 @import './modules/variables';
 
 $dist: './fonts/Montserrat-Regular';
diff --git a/src/Web/WebSPA/Client/index.html b/src/Web/WebSPA/Client/index.html
new file mode 100644
index 000000000..5d9432f70
--- /dev/null
+++ b/src/Web/WebSPA/Client/index.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>eShopConContainers.WebSPA</title>
+  <base href="/">
+
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="icon" type="image/x-icon" href="favicon.ico">
+</head>
+<body>
+  <esh-app>
+    <div class="preloading">
+      <i class="fa fa-spinner fa-spin fa-5x" aria-hidden="true"></i>
+    </div>
+  </esh-app>
+</body>
+</html>
diff --git a/src/Web/WebSPA/Client/main.ts b/src/Web/WebSPA/Client/main.ts
index ff50b8628..1f87381f8 100644
--- a/src/Web/WebSPA/Client/main.ts
+++ b/src/Web/WebSPA/Client/main.ts
@@ -1,23 +1,11 @@
-import './polyfills';
-
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 
-import { AppModule }              from './modules/app.module';
+import { AppModule } from './modules/app.module';
+import { environment } from './environments/environment';
 
-if (process.env.ENV === 'Development') {
-  // Development
-} else {
-  // Production
+if (environment.production) {
   enableProdMode();
 }
 
 platformBrowserDynamic().bootstrapModule(AppModule);
-
-// Basic hot reloading support. Automatically reloads and restarts the Angular 2 app each time
-// you modify source files. This will not preserve any application state other than the URL.
-declare var module: any;
-
-if (module.hot) {
-    module.hot.accept();
-}
diff --git a/src/Web/WebSPA/Client/modules/app.component.html b/src/Web/WebSPA/Client/modules/app.component.html
index bd50dc066..14e322e61 100644
--- a/src/Web/WebSPA/Client/modules/app.component.html
+++ b/src/Web/WebSPA/Client/modules/app.component.html
@@ -4,7 +4,7 @@
 
             <section class="col-lg-7 col-md-6 col-xs-12">
                 <a class="navbar-brand" routerLink="catalog">
-                    <img src="../images/brand.png" />
+                    <img src="assets/images/brand.png" />
                 </a>
             </section>
 
@@ -28,7 +28,7 @@
         <article class="row">
 
             <section class="col-sm-6">
-                <img class="esh-app-footer-brand" src="../images/brand_dark.png" />
+                <img class="esh-app-footer-brand" src="assets/images/brand_dark.png" />
             </section>
 
             <section class="col-sm-6">
diff --git a/src/Web/WebSPA/Client/modules/app.component.ts b/src/Web/WebSPA/Client/modules/app.component.ts
index 00abe4ad4..37bba914b 100644
--- a/src/Web/WebSPA/Client/modules/app.component.ts
+++ b/src/Web/WebSPA/Client/modules/app.component.ts
@@ -13,12 +13,12 @@ import { ConfigurationService } from './shared/services/configuration.service';
  */
 
 @Component({
-    selector: 'esh-app.esh-app',
+    selector: 'esh-app',
     styleUrls: ['./app.component.scss'],
     templateUrl: './app.component.html'
 })
 export class AppComponent implements OnInit {
-    private Authenticated: boolean = false;
+    Authenticated: boolean = false;
     subscription: Subscription;
 
     constructor(private titleService: Title, private securityService: SecurityService, private configurationService: ConfigurationService) {
diff --git a/src/Web/WebSPA/Client/modules/basket/basket-status/basket-status.component.html b/src/Web/WebSPA/Client/modules/basket/basket-status/basket-status.component.html
index 7509a2281..b15127a60 100644
--- a/src/Web/WebSPA/Client/modules/basket/basket-status/basket-status.component.html
+++ b/src/Web/WebSPA/Client/modules/basket/basket-status/basket-status.component.html
@@ -3,7 +3,7 @@
    [routerLink]="['basket']">
 
     <div class="esh-basketstatus-image">
-        <img src="../../../images/cart.png" />
+        <img src="assets/images/cart.png" />
     </div>
     <div class="esh-basketstatus-badge">
         {{badge}}
diff --git a/src/Web/WebSPA/Client/modules/catalog/catalog.component.html b/src/Web/WebSPA/Client/modules/catalog/catalog.component.html
index c0aa46ad5..d3c40af62 100644
--- a/src/Web/WebSPA/Client/modules/catalog/catalog.component.html
+++ b/src/Web/WebSPA/Client/modules/catalog/catalog.component.html
@@ -16,7 +16,7 @@
                 <option *ngFor="let type of types" [value]="type.id">{{type.type}}</option>
             </select>
         </label>
-        <img class="esh-catalog-send" (click)="onFilterApplied($event)" src="../../images/arrow-right.svg" />
+        <img class="esh-catalog-send" (click)="onFilterApplied($event)" src="/assets/images/arrow-right.svg" />
     </div>
 </section>
 
diff --git a/src/Web/WebSPA/Client/modules/catalog/catalog.component.scss b/src/Web/WebSPA/Client/modules/catalog/catalog.component.scss
index 663215ff6..9158bf858 100644
--- a/src/Web/WebSPA/Client/modules/catalog/catalog.component.scss
+++ b/src/Web/WebSPA/Client/modules/catalog/catalog.component.scss
@@ -4,7 +4,7 @@
     $banner-height: 260px;
 
     &-hero {
-        background-image: url('../../images/main_banner.png');
+        background-image: url('../../assets/images/main_banner.png');
         background-size: cover;
         height: $banner-height;
         width: 100%;
@@ -61,7 +61,7 @@
         }
 
         &::after {
-            background-image: url('../../images/arrow-down.png');
+            background-image: url('../../assets/images/arrow-down.png');
             content: '';
             height: 7px; //png height
             position: absolute;
diff --git a/src/Web/WebSPA/Client/modules/orders/orders-detail/orders-detail.component.ts b/src/Web/WebSPA/Client/modules/orders/orders-detail/orders-detail.component.ts
index 31da11594..c9c5c79c0 100644
--- a/src/Web/WebSPA/Client/modules/orders/orders-detail/orders-detail.component.ts
+++ b/src/Web/WebSPA/Client/modules/orders/orders-detail/orders-detail.component.ts
@@ -1,6 +1,6 @@
-import { Component, OnInit }    from '@angular/core';
-import { OrdersService }        from '../orders.service';
-import { IOrder }               from '../../shared/models/order.model';
+import { Component, OnInit } from '@angular/core';
+import { OrdersService } from '../orders.service';
+import { IOrderDetail } from '../../shared/models/order-detail.model';
 import { ActivatedRoute } from '@angular/router';
 
 @Component({
@@ -9,7 +9,7 @@ import { ActivatedRoute } from '@angular/router';
     templateUrl: './orders-detail.component.html'
 })
 export class OrdersDetailComponent implements OnInit {
-    order = {}; // new order
+    public order: IOrderDetail = <IOrderDetail>{};
 
     constructor(private service: OrdersService, private route: ActivatedRoute) { }
 
@@ -27,5 +27,4 @@ export class OrdersDetailComponent implements OnInit {
             console.log(this.order);
         });
     }
-}
-
+}
\ No newline at end of file
diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts
index 8f8a10eb0..4f6f82a9f 100644
--- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts
+++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts
@@ -13,10 +13,10 @@ import { Router }                                   from '@angular/router';
     templateUrl: './orders-new.component.html'
 })
 export class OrdersNewComponent implements OnInit {
-    private newOrderForm: FormGroup;  // new order form
-    private isOrderProcessing: Boolean;
-    private errorReceived: Boolean;
-    private order: IOrder;
+    newOrderForm: FormGroup;  // new order form
+    isOrderProcessing: boolean;
+    errorReceived: boolean;
+    order: IOrder;
 
     constructor(private service: OrdersService, fb: FormBuilder, private router: Router) {
         // Obtain user profile information
diff --git a/src/Web/WebSPA/Client/modules/orders/orders.service.ts b/src/Web/WebSPA/Client/modules/orders/orders.service.ts
index 24991056a..355670cdd 100644
--- a/src/Web/WebSPA/Client/modules/orders/orders.service.ts
+++ b/src/Web/WebSPA/Client/modules/orders/orders.service.ts
@@ -4,6 +4,7 @@ import { Response } from '@angular/http';
 import { DataService } from '../shared/services/data.service';
 import { IOrder } from '../shared/models/order.model';
 import { IOrderItem } from '../shared/models/orderItem.model';
+import { IOrderDetail } from "../shared/models/order-detail.model";
 import { SecurityService } from '../shared/services/security.service';
 import { ConfigurationService } from '../shared/services/configuration.service';
 import { BasketWrapperService } from '../shared/services/basket.wrapper.service';
@@ -35,7 +36,7 @@ export class OrdersService {
         });
     }
 
-    getOrder(id: number): Observable<IOrder> {
+    getOrder(id: number): Observable<IOrderDetail> {
         let url = this.ordersUrl + '/api/v1/orders/' + id;
 
         return this.service.get(url).map((response: Response) => {
diff --git a/src/Web/WebSPA/Client/modules/shared/components/identity/identity.html b/src/Web/WebSPA/Client/modules/shared/components/identity/identity.html
index f15dda395..9dce33adc 100644
--- a/src/Web/WebSPA/Client/modules/shared/components/identity/identity.html
+++ b/src/Web/WebSPA/Client/modules/shared/components/identity/identity.html
@@ -12,7 +12,7 @@
          *ngIf="authenticated">
 
         <div class="esh-identity-name">{{userName}}</div>
-        <img class="esh-identity-image" src="../../../../images/arrow-down.png">
+        <img class="esh-identity-image" src="assets/images/arrow-down.png">
     </section>
 
     <section class="esh-identity-drop"
@@ -22,14 +22,14 @@
              [routerLink]="['orders']">
 
             <div class="esh-identity-name esh-identity-name--upper">My orders</div>
-            <img class="esh-identity-image" src="../../../../images/my_orders.png">
+            <img class="esh-identity-image" src="assets/images/my_orders.png">
         </div>
 
         <div class="esh-identity-item"
              (click)="logoutClicked($event)">
 
             <div class="esh-identity-name esh-identity-name--upper">Log Out</div>
-            <img class="esh-identity-image" src="../../../../images/logout.png">
+            <img class="esh-identity-image" src="assets/images/logout.png">
         </div>
     </section>
 </div>
diff --git a/src/Web/WebSPA/Client/modules/shared/components/identity/identity.ts b/src/Web/WebSPA/Client/modules/shared/components/identity/identity.ts
index 606249c0a..505cdc05d 100644
--- a/src/Web/WebSPA/Client/modules/shared/components/identity/identity.ts
+++ b/src/Web/WebSPA/Client/modules/shared/components/identity/identity.ts
@@ -10,7 +10,7 @@ import { SecurityService } from  '../../services/security.service';
     styleUrls: ['./identity.scss']
 })
 export class Identity implements OnInit  {
-    private authenticated: boolean = false;
+    authenticated: boolean = false;
     private subscription: Subscription;
     private userName: string = '';
 
diff --git a/src/Web/WebSPA/Client/modules/shared/models/order-detail.model.ts b/src/Web/WebSPA/Client/modules/shared/models/order-detail.model.ts
new file mode 100644
index 000000000..25a869f9c
--- /dev/null
+++ b/src/Web/WebSPA/Client/modules/shared/models/order-detail.model.ts
@@ -0,0 +1,14 @@
+import {IOrderItem} from './orderItem.model';
+
+export interface IOrderDetail {
+    ordernumber: string;
+    status: string;
+    street: string;
+    date: Date;
+    city: number;
+    state: string;
+    zipcode: string;
+    country: number;
+    total: number;
+    orderitems: IOrderItem[];
+}
diff --git a/src/Web/WebSPA/Client/modules/shared/shared.module.ts b/src/Web/WebSPA/Client/modules/shared/shared.module.ts
index 64441396f..da7667df6 100644
--- a/src/Web/WebSPA/Client/modules/shared/shared.module.ts
+++ b/src/Web/WebSPA/Client/modules/shared/shared.module.ts
@@ -16,6 +16,10 @@ import { StorageService } from './services/storage.service';
 import { Pager } from './components/pager/pager';
 import { Header } from './components/header/header';
 import { Identity } from './components/identity/identity';
+import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
+
+// Pipes:
+import { UppercasePipe } from './pipes/uppercase.pipe';
 
 @NgModule({
     imports: [
@@ -31,7 +35,9 @@ import { Identity } from './components/identity/identity';
     declarations: [
         Pager,
         Header,
-        Identity
+        Identity,
+        PageNotFoundComponent,
+        UppercasePipe
     ],
     exports: [
         // Modules
@@ -43,7 +49,9 @@ import { Identity } from './components/identity/identity';
         // Providers, Components, directive, pipes
         Pager,
         Header,
-        Identity
+        Identity,
+        PageNotFoundComponent,
+        UppercasePipe
     ]
 })
 export class SharedModule {
diff --git a/src/Web/WebSPA/Client/polyfills.ts b/src/Web/WebSPA/Client/polyfills.ts
index 2cdf1a036..53bdaf1b8 100644
--- a/src/Web/WebSPA/Client/polyfills.ts
+++ b/src/Web/WebSPA/Client/polyfills.ts
@@ -1,23 +1,68 @@
-// Added parts of es6 which are necessary for your project or your browser support requirements.
-import 'core-js/es6/symbol';
-import 'core-js/es6/object';
-import 'core-js/es6/function';
-import 'core-js/es6/parse-int';
-import 'core-js/es6/parse-float';
-import 'core-js/es6/number';
-import 'core-js/es6/math';
-import 'core-js/es6/string';
-import 'core-js/es6/date';
-import 'core-js/es6/array';
-import 'core-js/es6/regexp';
-import 'core-js/es6/map';
-import 'core-js/es6/set';
-import 'core-js/es6/weak-map';
-import 'core-js/es6/weak-set';
-import 'core-js/es6/typed';
-import 'core-js/es6/reflect';
-// see issue https://github.com/AngularClass/angular2-webpack-starter/issues/709
-// import 'core-js/es6/promise';
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ *      file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
 
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+// import 'core-js/es6/symbol';
+// import 'core-js/es6/object';
+// import 'core-js/es6/function';
+// import 'core-js/es6/parse-int';
+// import 'core-js/es6/parse-float';
+// import 'core-js/es6/number';
+// import 'core-js/es6/math';
+// import 'core-js/es6/string';
+// import 'core-js/es6/date';
+// import 'core-js/es6/array';
+// import 'core-js/es6/regexp';
+// import 'core-js/es6/map';
+// import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js';  // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following to support `@angular/animation`. */
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+
+/** Evergreen browsers require these. **/
+import 'core-js/es6/reflect';
 import 'core-js/es7/reflect';
-import 'zone.js/dist/zone';
+
+
+/** ALL Firefox browsers require the following to support `@angular/animation`. **/
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js/dist/zone';  // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+
+/**
+ * Date, currency, decimal and percent pipes.
+ * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+ */
+// import 'intl';  // Run `npm install --save intl`.
diff --git a/src/Web/WebSPA/Client/test.ts b/src/Web/WebSPA/Client/test.ts
new file mode 100644
index 000000000..9bf72267e
--- /dev/null
+++ b/src/Web/WebSPA/Client/test.ts
@@ -0,0 +1,32 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare var __karma__: any;
+declare var require: any;
+
+// Prevent Karma from running prematurely.
+__karma__.loaded = function () {};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.start();
diff --git a/src/Web/WebSPA/Client/tsconfig.app.json b/src/Web/WebSPA/Client/tsconfig.app.json
new file mode 100644
index 000000000..5e2507db5
--- /dev/null
+++ b/src/Web/WebSPA/Client/tsconfig.app.json
@@ -0,0 +1,13 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/app",
+    "module": "es2015",
+    "baseUrl": "",
+    "types": []
+  },
+  "exclude": [
+    "test.ts",
+    "**/*.spec.ts"
+  ]
+}
diff --git a/src/Web/WebSPA/Client/tsconfig.spec.json b/src/Web/WebSPA/Client/tsconfig.spec.json
new file mode 100644
index 000000000..510e3f1fd
--- /dev/null
+++ b/src/Web/WebSPA/Client/tsconfig.spec.json
@@ -0,0 +1,20 @@
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/spec",
+    "module": "commonjs",
+    "target": "es5",
+    "baseUrl": "",
+    "types": [
+      "jasmine",
+      "node"
+    ]
+  },
+  "files": [
+    "test.ts"
+  ],
+  "include": [
+    "**/*.spec.ts",
+    "**/*.d.ts"
+  ]
+}
diff --git a/src/Web/WebSPA/Client/typings.d.ts b/src/Web/WebSPA/Client/typings.d.ts
new file mode 100644
index 000000000..ef5c7bd62
--- /dev/null
+++ b/src/Web/WebSPA/Client/typings.d.ts
@@ -0,0 +1,5 @@
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+  id: string;
+}
diff --git a/src/Web/WebSPA/Client/vendor.ts b/src/Web/WebSPA/Client/vendor.ts
deleted file mode 100644
index 4f40d6b4a..000000000
--- a/src/Web/WebSPA/Client/vendor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// For vendors for example jQuery, Lodash, angular2-jwt just import them here unless you plan on
-// chunking vendors files for async loading. You would need to import the async loaded vendors
-// at the entry point of the async loaded file. Also see custom-typings.d.ts as you also need to
-// run `typings install x` where `x` is your module
-
-// Angular 2
-import '@angular/platform-browser';
-import '@angular/platform-browser-dynamic';
-import '@angular/core';
-import '@angular/common';
-import '@angular/forms';
-import '@angular/http';
-import '@angular/router';
-
-// RxJS
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/mergeMap';
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/finally';
-import 'rxjs/add/observable/throw';
diff --git a/src/Web/WebSPA/Server/Controllers/HomeController.cs b/src/Web/WebSPA/Server/Controllers/HomeController.cs
index 7e78cd41a..3bb021943 100644
--- a/src/Web/WebSPA/Server/Controllers/HomeController.cs
+++ b/src/Web/WebSPA/Server/Controllers/HomeController.cs
@@ -1,6 +1,5 @@
 // For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
 
-using System.Linq;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
@@ -18,23 +17,6 @@ namespace eShopConContainers.WebSPA.Server.Controllers
             _env = env;
             _settings = settings;
         }
-
-        public IActionResult Index()
-        {
-            ViewBag.HashedMain = GetHashedMainDotJs();
-
-            return View();
-        }
-
-        public string GetHashedMainDotJs()
-        {
-            var basePath = _env.WebRootPath + "//dist//";
-            var info = new System.IO.DirectoryInfo(basePath);
-            var file = info.GetFiles().Where(f => f.Name.StartsWith("main.") && !f.Name.EndsWith("bundle.map")).FirstOrDefault();
-
-            return file.Name;
-        }
-
         public IActionResult Configuration()
         {
             return Json(_settings.Value);
diff --git a/src/Web/WebSPA/Startup.cs b/src/Web/WebSPA/Startup.cs
index a572f7961..f06cb8ee1 100644
--- a/src/Web/WebSPA/Startup.cs
+++ b/src/Web/WebSPA/Startup.cs
@@ -1,16 +1,15 @@
 using System;
+using System.IO;
 using Microsoft.AspNetCore.Antiforgery;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.SpaServices.Webpack;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.HealthChecks;
 using Newtonsoft.Json.Serialization;
 using eShopOnContainers.WebSPA;
-using Microsoft.Extensions.HealthChecks;
-using System.Threading.Tasks;
 
 namespace eShopConContainers.WebSPA
 {
@@ -76,29 +75,34 @@ namespace eShopConContainers.WebSPA
 
             // Configure XSRF middleware, This pattern is for SPA style applications where XSRF token is added on Index page 
             // load and passed back token on every subsequent async request            
+            // app.Use(async (context, next) =>
+            // {
+            //     if (string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase))
+            //     {
+            //         var tokens = antiforgery.GetAndStoreTokens(context);
+            //         context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
+            //     }
+            //     await next.Invoke();
+            // });
+
             app.Use(async (context, next) =>
             {
-                if (string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase))
+                await next();
+
+                // If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
+                // Rewrite request to use app root
+                if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value) && !context.Request.Path.Value.StartsWith("/api"))
                 {
-                    var tokens = antiforgery.GetAndStoreTokens(context);
-                    context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
+                    context.Request.Path = "/index.html"; 
+                    context.Response.StatusCode = 200; // Make sure we update the status code, otherwise it returns 404
+                    await next();
                 }
-                await next.Invoke();
             });
 
+            app.UseDefaultFiles();
             app.UseStaticFiles();
 
-  
-            app.UseMvc(routes =>
-            {
-                routes.MapRoute(
-                    name: "default",
-                    template: "{controller=Home}/{action=Index}/{id?}");
-
-                routes.MapSpaFallbackRoute(
-                    name: "spa-fallback",
-                    defaults: new { controller = "Home", action = "Index" });
-            });
+            app.UseMvcWithDefaultRoute();
         }
     }
 }
diff --git a/src/Web/WebSPA/Views/Home/Index.cshtml b/src/Web/WebSPA/Views/Home/Index.cshtml
deleted file mode 100644
index 931ef55c7..000000000
--- a/src/Web/WebSPA/Views/Home/Index.cshtml
+++ /dev/null
@@ -1,12 +0,0 @@
-<esh-app class="esh-app" asp-prerender-webpack-config="config/webpack.config.js">
-    <div class="preloading">
-        <i class="fa fa-spinner fa-spin fa-5x" aria-hidden="true"></i>
-    </div>
-</esh-app>
-
-<script>
-    window.user = '@ViewBag.user';
-</script>
-
-<script src="~/dist/vendor.js" asp-append-version="true"></script>
-<script src="~/dist/@ViewBag.HashedMain" asp-append-version="true"></script>
diff --git a/src/Web/WebSPA/Views/Shared/_Layout.cshtml b/src/Web/WebSPA/Views/Shared/_Layout.cshtml
deleted file mode 100644
index c7d974e27..000000000
--- a/src/Web/WebSPA/Views/Shared/_Layout.cshtml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>eShopConContainers.WebSPA</title>
-    <base href="/" />
-    <link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
-</head>
-
-<body>
-    @RenderBody()
-</body>
-
-</html>
\ No newline at end of file
diff --git a/src/Web/WebSPA/Views/_ViewImports.cshtml b/src/Web/WebSPA/Views/_ViewImports.cshtml
deleted file mode 100644
index 1c0d391f9..000000000
--- a/src/Web/WebSPA/Views/_ViewImports.cshtml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
-@addTagHelper "*, Microsoft.AspNetCore.SpaServices"
diff --git a/src/Web/WebSPA/Views/_ViewStart.cshtml b/src/Web/WebSPA/Views/_ViewStart.cshtml
deleted file mode 100644
index 820a2f6e0..000000000
--- a/src/Web/WebSPA/Views/_ViewStart.cshtml
+++ /dev/null
@@ -1,3 +0,0 @@
-@{
-    Layout = "_Layout";
-}
diff --git a/src/Web/WebSPA/WebSPA.csproj b/src/Web/WebSPA/WebSPA.csproj
index d5161d753..cebe4377d 100644
--- a/src/Web/WebSPA/WebSPA.csproj
+++ b/src/Web/WebSPA/WebSPA.csproj
@@ -22,21 +22,9 @@
     <Content Update="appsettings.json;">
       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
     </Content>
-    <Content Update="Client\**\*;">
-      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-    </Content>
-    <Content Update="Views\**\*;">
-      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-    </Content>
-    <Content Update="tsconfig.json;">
-      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-    </Content>
     <Content Update="web.config;">
       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
     </Content>
-    <Content Update="config\**\*;">
-      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-    </Content>
     <Content Update="wwwroot\**\*;">
       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
     </Content>
@@ -51,7 +39,6 @@
     <PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="1.1.0" />
     <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="1.1.0" />
     <PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
-    <PackageReference Include="Webpack" Version="3.0.0" />
     <PackageReference Include="Microsoft.AspNetCore.AngularServices" Version="1.0.0-beta-000014" />
     <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.0" />
     <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.0" />
@@ -87,7 +74,6 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
     <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
   </ItemGroup>
 
diff --git a/src/Web/WebSPA/config/helpers.js b/src/Web/WebSPA/config/helpers.js
deleted file mode 100644
index 9d37e78a8..000000000
--- a/src/Web/WebSPA/config/helpers.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * @author: @AngularClass
- */
-
-var path = require('path');
-
-// Helper functions
-var ROOT = path.resolve(__dirname, '..');
-
-console.log('root directory:', root() + '\n');
-
-function hasProcessFlag(flag) {
-  return process.argv.join('').indexOf(flag) > -1;
-}
-
-function root(args) {
-  args = Array.prototype.slice.call(arguments, 0);
-  return path.join.apply(path, [ROOT].concat(args));
-}
-
-
-exports.hasProcessFlag = hasProcessFlag;
-exports.root = root;
diff --git a/src/Web/WebSPA/config/webpack.config.dev.js b/src/Web/WebSPA/config/webpack.config.dev.js
deleted file mode 100644
index d87a27ee4..000000000
--- a/src/Web/WebSPA/config/webpack.config.dev.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
-    //devtool: 'cheap-module-source-map'
-};
diff --git a/src/Web/WebSPA/config/webpack.config.js b/src/Web/WebSPA/config/webpack.config.js
deleted file mode 100644
index 2bf3a804d..000000000
--- a/src/Web/WebSPA/config/webpack.config.js
+++ /dev/null
@@ -1,77 +0,0 @@
-var path = require('path');
-var webpack = require('webpack');
-var merge = require('extendify')({ isDeep: true, arrays: 'concat' });
-var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var extractCSS = new ExtractTextPlugin('styles.css');
-var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;
-var devConfig = require('./webpack.config.dev');
-var prodConfig = require('./webpack.config.prod');
-var CopyWebpackPlugin = require('copy-webpack-plugin');
-var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Development';
-
-console.log("==========Dev Mode = " + isDevelopment + " ============" )
-
-module.exports = merge({
-    resolve: {
-        extensions: ['.js', '.ts']
-    },
-    module: {
-        rules: [
-            {
-                test: /\.ts$/, exclude: [/\.(spec|e2e)\.ts$/],
-                loaders: ['awesome-typescript-loader?forkChecker=true ', 'angular2-template-loader', 'angular2-router-loader']
-            },
-            { test: /\.html$/, loader: "html" },
-            { test: /\.scss$/, loader: 'exports-loader?module.exports.toString()!css-loader!sass-loader' },
-            { test: /\.json$/, loader: 'json-loader' },
-            {
-                test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
-                loader: "file-loader"
-            }, {
-                test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
-                loader: "file-loader"
-            }, {
-                test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
-                loader: "file-loader"
-            }, {
-                test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
-                loader: "file-loader"
-            }, 
-            {
-                test: /\.(png|jpg|gif|svg)$/,
-                loader: "file-loader?name=images/[name].[ext]"
-            }
-        ]
-    },
-    entry: {
-        'main': './Client/main.ts'
-    },
-    output: {
-        path: path.join(__dirname, '../wwwroot', 'dist'),
-        filename: '[name].js',
-        publicPath: '/dist/'
-    },
-    profile: true,
-    plugins: [
-        extractCSS,
-        new webpack.DllReferencePlugin({
-            context: __dirname,
-            manifest: require('../wwwroot/dist/vendor-manifest.json')
-        }),
-        // To eliminate warning
-        // https://github.com/AngularClass/angular2-webpack-starter/issues/993
-        new webpack.ContextReplacementPlugin(
-            /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
-            __dirname
-        ),
-        new ForkCheckerPlugin(),
-        new webpack.DefinePlugin({
-            'process.env': {
-                'ENV': JSON.stringify(process.env.ASPNETCORE_ENVIRONMENT)
-            }
-        }),
-         new CopyWebpackPlugin([
-            { from: 'Client/fonts', to: 'fonts' }
-         ])
-    ]
-}, isDevelopment ? devConfig : prodConfig);
diff --git a/src/Web/WebSPA/config/webpack.config.prod.js b/src/Web/WebSPA/config/webpack.config.prod.js
deleted file mode 100644
index 95277c818..000000000
--- a/src/Web/WebSPA/config/webpack.config.prod.js
+++ /dev/null
@@ -1,23 +0,0 @@
-var webpack = require('webpack');
-const WebpackMd5Hash = require('webpack-md5-hash');
-
-module.exports = {
-    devtool: 'source-map',
-    output: {
-        filename: '[name].[chunkhash].bundle.js',
-        sourceMapFilename: '[name].[chunkhash].bundle.map',
-        chunkFilename: '[id].[chunkhash].chunk.js'
-    },
-    plugins: [
-        // new webpack.LoaderOptionsPlugin({
-        //     minimize: true,
-        //     debug: false
-        // }),
-        new WebpackMd5Hash(),
-        new webpack.optimize.UglifyJsPlugin({
-            beautify: false,
-            comments: false,
-            sourceMap: true
-        })
-    ]
-};
diff --git a/src/Web/WebSPA/config/webpack.config.vendor.js b/src/Web/WebSPA/config/webpack.config.vendor.js
deleted file mode 100644
index 9009e8ba6..000000000
--- a/src/Web/WebSPA/config/webpack.config.vendor.js
+++ /dev/null
@@ -1,74 +0,0 @@
-var path = require('path');
-var webpack = require('webpack');
-var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var extractCSS = new ExtractTextPlugin('vendor.css');
-var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Development';
-
-module.exports = {
-    resolve: {
-        extensions: ['.js']
-    },
-    module: {
-        rules: [
-            { test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' },
-            { test: /\.scss$/i, loader: extractCSS.extract(['css?minimize', 'sass']) },
-            { test: /\.json$/, loader: 'json-loader' }
-        ]
-    },
-    entry: {
-        // polyfills: [
-        //     'core-js/es6/symbol',
-        //     'core-js/es6/object',
-        //     'core-js/es6/function',
-        //     'core-js/es6/parse-int',
-        //     'core-js/es6/parse-float',
-        //     'core-js/es6/number',
-        //     'core-js/es6/math',
-        //     'core-js/es6/string',
-        //     'core-js/es6/date',
-        //     'core-js/es6/array',
-        //     'core-js/es6/regexp',
-        //     'core-js/es6/map',
-        //     'core-js/es6/set',
-        //     'core-js/es6/reflect',
-        //     'core-js/es7/reflect',
-        //     'zone.js/dist/zone'
-        // ],
-        vendor: [
-            'font-awesome/scss/font-awesome.scss',
-            'bootstrap/scss/bootstrap.scss',
-            '@angular/common',
-            '@angular/compiler',
-            '@angular/core',
-            '@angular/http',
-            '@angular/forms',
-            '@angular/platform-browser',
-            '@angular/platform-browser-dynamic',
-            '@angular/router',
-            './Client/globals.scss'
-        ]
-    },
-    output: {
-        path: path.join(__dirname, '../wwwroot', 'dist'),
-        filename: '[name].js',
-        library: '[name]_[hash]',
-    },
-    plugins: [
-        extractCSS,
-        // To eliminate warning
-        // https://github.com/AngularClass/angular2-webpack-starter/issues/993
-        new webpack.ContextReplacementPlugin(
-            /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
-            __dirname
-        ),
-        new webpack.DllPlugin({
-            path: path.join(__dirname, '../wwwroot', 'dist', '[name]-manifest.json'),
-            name: '[name]_[hash]'
-        })
-    ].concat(isDevelopment ? [] : [
-        new webpack.optimize.UglifyJsPlugin({
-            beautify: false,
-            comments: false
-        })
-    ])
-};
diff --git a/src/Web/WebSPA/package.json b/src/Web/WebSPA/package.json
index b863e2f1c..003f220ac 100644
--- a/src/Web/WebSPA/package.json
+++ b/src/Web/WebSPA/package.json
@@ -16,90 +16,51 @@
     "email": "cesardl@microsoft.com"
   },
   "scripts": {
+    "ng": "ng",
     "rimraf": "rimraf",
-    "typings": "typings",
-    "webpack": "webpack",
-    "clean": "npm cache clean && npm run rimraf -- node_modules doc typings coverage wwwroot/dist",
-    "clean:dist": "npm run rimraf -- wwwroot/dist",
-    "preclean:install": "npm run clean",
-    "clean:install": "npm set progress=false && npm install",
-    "preclean:start": "npm run clean",
-    "clean:start": "npm start",
-    "build:vendor": "node node_modules/webpack/bin/webpack.js --config config/webpack.config.vendor.js",
-    "build:main": "node node_modules/webpack/bin/webpack.js --config config/webpack.config.js",
-    "setdev": "set ASPNETCORE_ENVIRONMENT=Development",
-    "setprod": "set ASPNETCORE_ENVIRONMENT=Production",
-    "build:dev": "npm run setdev && npm run clean:dist && npm run build:vendor && npm run build:main",
-    "build:prod": "npm run setprod && npm run clean:dist && npm run build:vendor && npm run build:main",
-    "version": "npm run build",
+    "clean": "npm cache clean && npm run rimraf -- node_modules doc typings coverage wwwroot",
+    "start": "ng serve",
+    "build:dev": "ng build",
+    "build:prod": "ng build --prod --aot --extract-css",
     "lint:sass": "sass-lint -c .sass-lint.yml Client/**/*.scss --verbose",
     "lint:ts": "tslint -c tslint.json Client/**/*.ts"
   },
   "dependencies": {
-    "@angular/common": "2.1.2",
-    "@angular/compiler": "2.1.2",
-    "@angular/compiler-cli": "2.1.2",
-    "@angular/core": "2.1.2",
-    "@angular/forms": "2.1.2",
-    "@angular/http": "2.1.2",
-    "@angular/platform-browser": "2.1.2",
-    "@angular/platform-browser-dynamic": "2.1.2",
-    "@angular/platform-server": "2.1.2",
-    "@angular/router": "3.1.2",
-    "@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.11",
-    "aspnet-prerendering": "1.0.7",
-    "aspnet-webpack": "1.0.24",
+    "@angular/common": "^4.0.0",
+    "@angular/compiler": "^4.0.0",
+    "@angular/core": "^4.0.0",
+    "@angular/forms": "^4.0.0",
+    "@angular/http": "^4.0.0",
+    "@angular/platform-browser": "^4.0.0",
+    "@angular/platform-browser-dynamic": "^4.0.0",
+    "@angular/router": "^4.0.0",
+    "@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.22",
     "bootstrap": "4.0.0-alpha.5",
-    "core-js": "2.4.1",
+    "core-js": "^2.4.1",
     "file-loader": "0.9.0",
     "font-awesome": "4.6.3",
     "isomorphic-fetch": "2.2.1",
     "normalize.css": "5.0.0",
     "preboot": "4.5.2",
-    "rxjs": "5.0.0-beta.12",
-    "zone.js": "0.6.26"
+    "rxjs": "^5.1.0",
+    "zone.js": "^0.8.4"
   },
   "devDependencies": {
+    "@angular/cli": "1.0.0",
+    "@angular/compiler-cli": "^4.0.0",
+    "@types/jasmine": "2.5.38",
+    "@types/node": "~6.0.60",
     "@types/core-js": "0.9.34",
     "@types/hammerjs": "2.0.33",
-    "@types/jasmine": "2.5.35",
-    "@types/node": "6.0.45",
     "@types/protractor": "1.5.20",
     "@types/selenium-webdriver": "2.44.26",
-    "@types/sinon": "1.16.31",
-    "@types/source-map": "0.1.28",
-    "@types/uglify-js": "2.6.28",
-    "@types/webpack": "1.12.35",
-    "angular2-router-loader": "0.3.4",
-    "angular2-template-loader": "0.6.0",
-    "awesome-typescript-loader": "2.2.4",
-    "codelyzer": "1.0.0-beta.3",
-    "copy-webpack-plugin": "4.0.1",
-    "css": "2.2.1",
-    "css-loader": "0.25.0",
-    "es6-promise": "3.2.1",
-    "es6-promise-loader": "1.0.2",
-    "exports-loader": "0.6.3",
-    "extendify": "1.0.0",
-    "extract-text-webpack-plugin": "2.0.0-beta.4",
-    "file-loader": "0.9.0",
-    "html-loader": "0.4.4",
-    "html-webpack-plugin": "2.24.1",
-    "json-loader": "0.5.4",
-    "node-sass": "4.5.0",
-    "parse5": "2.1.5",
-    "rimraf": "2.5.4",
+    "codelyzer": "~2.0.0",
     "sass-lint": "1.10.2",
-    "sass-loader": "4.0.2",
     "ts-helpers": "1.1.1",
-    "ts-node": "1.4.3",
-    "tslint": "3.15.1",
+    "ts-node": "~2.0.0",
+    "tslint": "~4.5.0",
     "typedoc": "0.5.0",
-    "typescript": "2.0.6",
-    "url-loader": "0.5.7",
-    "webpack": "2.1.0-beta.25",
-    "webpack-externals-plugin": "1.0.0",
-    "webpack-hot-middleware": "2.13.0",
-    "webpack-md5-hash": "0.0.5"
+    "typescript": "~2.2.0",
+    "url-loader": "0.5.7"
   }
 }
diff --git a/src/Web/WebSPA/tsconfig.json b/src/Web/WebSPA/tsconfig.json
index e04adfb21..a35a8ee3a 100644
--- a/src/Web/WebSPA/tsconfig.json
+++ b/src/Web/WebSPA/tsconfig.json
@@ -1,42 +1,20 @@
 {
+  "compileOnSave": false,
   "compilerOptions": {
-    "target": "es5",
-    "module": "commonjs",
-    "moduleResolution": "node",
+    "outDir": "./dist/out-tsc",
+    "baseUrl": "src",
+    "sourceMap": true,
     "declaration": false,
+    "moduleResolution": "node",
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
-    "allowSyntheticDefaultImports": true,
-    "sourceMap": true,
-    "strictNullChecks": false,
-    "baseUrl": "./src",
-    "paths": {},
-    "lib": [
-      "dom",
-      "es6"
+    "target": "es5",
+    "typeRoots": [
+      "node_modules/@types"
     ],
-    "types": [
-      "hammerjs",
-      "jasmine",
-      "node",
-      "protractor",
-      "selenium-webdriver",
-      "source-map",
-      "uglify-js",
-      "webpack"
+    "lib": [
+      "es2016",
+      "dom"
     ]
-  },
-  "exclude": [
-    "node_modules",
-    "wwwroot"
-  ],
-  "awesomeTypescriptLoaderOptions": {
-    "forkChecker": true,
-    "useWebpackText": true
-  },
-  "compileOnSave": false,
-  "buildOnSave": false,
-  "atom": {
-    "rewriteTsconfig": false
   }
-}
\ No newline at end of file
+}
diff --git a/src/Web/WebSPA/wwwroot/web.config b/src/Web/WebSPA/wwwroot/web.config
deleted file mode 100644
index e70a7778d..000000000
--- a/src/Web/WebSPA/wwwroot/web.config
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-  <system.webServer>
-    <handlers>
-      <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
-    </handlers>
-    <httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false"/>
-  </system.webServer>
-</configuration>
diff --git a/src/Web/WebStatus/WebStatus.csproj b/src/Web/WebStatus/WebStatus.csproj
index 47a3e20f0..e3028722d 100644
--- a/src/Web/WebStatus/WebStatus.csproj
+++ b/src/Web/WebStatus/WebStatus.csproj
@@ -21,7 +21,6 @@
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
-    <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
     <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
   </ItemGroup>
 
diff --git a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs
index 5f52e1771..5b2424114 100644
--- a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs
+++ b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs
@@ -45,7 +45,19 @@ namespace FunctionalTests.Services.Ordering
        
         string BuildOrder()
         {
+            List<OrderItemDTO> orderItemsList = new List<OrderItemDTO>();
+            orderItemsList.Add(new OrderItemDTO()
+                                                    {
+                                                        ProductId = 1,
+                                                        Discount = 8M,
+                                                        UnitPrice = 10,
+                                                        Units = 1,
+                                                        ProductName = "Some name"
+                                                    }
+                               );
+
             var order = new CreateOrderCommand(
+                orderItemsList,
                 cardExpiration: DateTime.UtcNow.AddYears(1),
                 cardNumber: "5145-555-5555",
                 cardHolderName: "Jhon Senna",
@@ -60,15 +72,6 @@ namespace FunctionalTests.Services.Ordering
                 buyerId: 3
             );
 
-            order.AddOrderItem(new OrderItemDTO()
-            {
-                ProductId = 1,
-                Discount = 8M,
-                UnitPrice = 10,
-                Units = 1,
-                ProductName = "Some name"
-            });
-
             return JsonConvert.SerializeObject(order);
         }       
     }
diff --git a/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs b/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs
index 3e2350c9d..49f04fa3b 100644
--- a/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs
+++ b/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs
@@ -9,7 +9,9 @@
     using System.Text;
     using System.Threading.Tasks;
     using Xunit;
+    using System.Collections;
     using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
+    using System.Collections.Generic;
 
     public class OrderingScenarios
         : OrderingScenarioBase
@@ -59,7 +61,19 @@
 
         string BuildOrder()
         {
+            List<OrderItemDTO> orderItemsList = new List<OrderItemDTO>();
+            orderItemsList.Add(new OrderItemDTO()
+                                                {
+                                                    ProductId = 1,
+                                                    Discount = 10M,
+                                                    UnitPrice = 10,
+                                                    Units = 1,
+                                                    ProductName = "Some name"
+                                                }
+                               );
+
             var order = new CreateOrderCommand(
+                orderItemsList,
                 cardExpiration: DateTime.UtcNow.AddYears(1),
                 cardNumber: "5145-555-5555",
                 cardHolderName: "Jhon Senna",
@@ -74,20 +88,12 @@
                 buyerId: 1               
             );
 
-            order.AddOrderItem(new OrderItemDTO()
-            {
-                ProductId = 1,
-                Discount = 10M,
-                UnitPrice = 10,
-                Units = 1,
-                ProductName = "Some name"
-            });
-
             return JsonConvert.SerializeObject(order);
         }
         string BuildOrderWithInvalidExperationTime()
         {
             var order = new CreateOrderCommand(
+                null,
                 cardExpiration: DateTime.UtcNow.AddYears(-1),
                 cardNumber: "5145-555-5555",
                 cardHolderName: "Jhon Senna",
diff --git a/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs
index 66070c497..2a4a356ec 100644
--- a/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs
+++ b/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs
@@ -70,6 +70,7 @@ namespace UnitTest.Ordering.Application
         private CreateOrderCommand FakeOrderRequest(Dictionary<string, object> args = null)
         {
             return new CreateOrderCommand(
+                null,
                 city: args != null && args.ContainsKey("city") ? (string)args["city"] : null,
                 street: args != null && args.ContainsKey("street") ? (string)args["street"] : null,
                 state: args != null && args.ContainsKey("state") ? (string)args["state"] : null,
diff --git a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
index 22760fc7d..9a4a70bf8 100644
--- a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
+++ b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
@@ -72,6 +72,7 @@ namespace UnitTest.Ordering.Application
         private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null)
         {
             return new CreateOrderCommand(
+                null,
                 city: args != null && args.ContainsKey("city") ? (string)args["city"] : null,
                 street: args != null && args.ContainsKey("street") ? (string)args["street"] : null,
                 state: args != null && args.ContainsKey("state") ? (string)args["state"] : null,