Skip to content
LearnVisualStudio edited this page Nov 16, 2014 · 4 revisions

Welcome to the HeroVsRobot wiki!

You can see this code at work at the URL:

http://www.HeroVsRobot.net

##Purpose##

The purpose of this project was:

(a) for me personally to learn more about Azure Websites, WebJobs, WebJob SDK, ASP.NET Identity and OAuth 2.0, Entity Framework Code First, Bootstrap 3, Github, Visual Studio's Team Explorer (Git tools), GitHub and more.

(b) so that I can use this project in my Azure Fundamentals series on Microsoft Virtual Academy.

(c) so that I can provide others (hopefully) with an easy to follow reference project that they can set up, modify and generally learn from.

##Design Decisions##

In order to make this accessible to everyone, I made a few important architectural / design decisions:

(1) I'm using ASP.NET Web Forms. Yes, ASP.NET MVC has all of the momentum right now, and I'll probably re-visit this application and create an MVC version of the project. However, I'm not unit testing which is one of the biggest arguments for using MVC. In terms of Separation of Concerns, yes MVC nudges you towards it, but I've seen plenty of violation of SoC in MVC projects. Likewise, I've seen plenty of WebForms projects that respect SoC. In fact, I've tried to do just that, keeping all domain logic and orchestration of domain models separated into classes contained in the following folders:

\Models

\Services

\DTOs

(2) I decided to not separate concerns between domain services (everything in the \Services folder) and the persistence layer. Instead, I relied on the Entity Framework inside of my services to perform CRUD operations. Yes, ideally, I would keep these separated for the same reasons I would honor SoC between the UI and domain layers.

However, with regards to the domain - persistence layer separation - YAGNI. I'm not going to re-write this to utilize another persistence layer. If something changes in the Azure Storage SDK or Entity Framework, this project is small enough that I can update it in an afternoon.

With regards to domain - presentation separation, I can foresee needing to add a Web Services API layer so that I could create a mobile / desktop client version of the app in the future.

(3) I used the ASP.NET Web Forms Project template which included plumbing for ASP.NET Identity and OAuth. Frankly, while I'm grateful that I didn't have to write all of that myself, it's not exactly a fool-proof implementation. I've gotten numerous reports of failures with users attempting to use social logins, or create accounts using email addresses with dash - characters. I've largely not touched that code at all if I could avoid it, except for some additions I made to the Account\Register.aspx and Account\RegisterExternalLogin.aspx pages where I create and persist a new instance of the Hero class.

##Architecture##

The architecture looks like this:

(1) After a new user registers or an existing player logs in, most of the action happens on the Default.aspx and Default.aspx.cs code files. Clicking a button or a maparea on the web form will call a helper method to perform the given task. That helper method makes a call into a service in the \Services folder. Each service works a little differently, but generally does the following:

1a. Validates that the player's Hero has enough turns and credits remaining.

1b. Calls a private helper method to perform the business logic of random rolls and outcomes that modify the Hero's attributes in some way and returns some result or status.

1c. The service saves the changes to the Hero entity.

1d. It bundles up an Activity object and sends it to the ActivityService. More about this in a moment.

1e. The service constructs a message (i.e, "Your hero's weapon has been improved 5 points.") and returns it to the caller in the form of an instance of Result (or BattleResult in the case of a battle).

Back in the Default.aspx.cs, the calling method receives the Result (or BattleResult) and calls a helper method to format the Result message for display utilizing Bootstrap 3 classes.

(2) Once an hour, the WebJobHourlyUpdate runs. It simply resets three properties of all Hero entities: TrainingLevel to 0, MovesRemaining to 25, Health to 50.

(3) In step 1d (above) ActivityService will place a message on an Azure Queue named 'activity'. The intent here is to log each player's actions for display on the CommandCenter.aspx page without slowing down the application / website as it attempts to perform an expensive write operation.

Now that the activity is queued, the WebJobActivityQueueProcessor plucks it from the queue and writes it to the activityHistory table, an Azure Table Storage table. Azure Table Storage is perfect for this because it is so cheap and players could potentially create a lot of data. Furthermore, since the data is partitioned by UserId, it makes retrieval for one single player very efficient.

This queue-centric approach is a popular cloud design pattern.

Finally, the CommandCenter.aspx page requests the last five actions from the activityHistory Azure Table for display.

Clone this wiki locally