An app from top to bottom

In this post I will take you through the stack of a real live app I have created, outlining the technology and structural choices I have made.

The reason for writing this is to give myself and others an overview of what it has involved to go from a blank canvas to a complete (minimum viable) SaaS product.

The application in question is OnCall, a small SaaS product I have created in my spare time and launched around a month ago. The app is basic, with the following key areas of functionality.

  • Team management
  • Scheduling of on-call shifts
  • Notifications and reminders
  • Personal schedules and dashboards
  • Account-related stuff (sign in, forgotten password)
  • Subscriptions and payment

Let's start by taking a look the application from a high level to get an idea of the parts.

OnCall overview

Basically the application consists of a public facing website, the application itself, a background worker and some integrations. All of the components rely on the same core library, which I will explain in more detail below.

The public website

This is the website you see at https://www.oncallschedule.com, it's just a simple website with a signup form. The tools and techniques used are the same as the application, so I won't delve into the details here, but just note that the website is run as an Azure Website, whereas the application is run as a web role.

The background worker

Some of the stuff that goes on inside OnCall isn't suitable for being handled by a web application. So things like calculating when to send notifications and reminders, as well as actually sending them is handled by a worker role.

When a user changes notification rules or a schedule, a message is queued to the background worker using Azure ServiceBus. The worker will then calculate whether there are any changes to the upcoming notification schedule for the account.

Communication between roles

Even though these tasks are handled separately the logic exists in the same domain model in the core library that is used by the application.

It wasn't until recently I discovered exactly how powerful Azure WebJobs are. The tasks performed by the background worker could easily be done by WebJobs. I will be looking into making the transition soon as it will bring down both complexity and cost.

The application

The application is where most of the fun stuff happens. It's what you see when visit https://mysubdomain.oncallschedule.com. The basic structure looks something like this.

Application architecture

  1. The user interacts with an AngularJS application
  2. The application is served by an MVC site that has no job except authentication and serving pages
  3. All actual interaction with the application happens through the API
  4. When data is required the API runs read-only queries
  5. The API invokes changes by sending commands
  6. Commands are handled by command handlers which have access to the domain models
  7. All business logic reside in the domain models
  8. The domain model might invoke events upon change
  9. Event handlers act upon these events to perform tasks like sending e-mails etc.

Technology and frameworks used

Frontend
AngularJS https://angularjs.org/ The frontend is a set of AngularJS applications.
Bootstrap http://getbootstrap.com/ The layout is created using the Bootstrap library
MomentJS http://momentjs.com Used for handling date and time operations
jQuery http://www.jquery.com Required for some UI plugins I use
Backend
MVC https://www.asp.net Pages are served by a basic MVC 5 application
WebAPI http://www.asp.net Interaction with the server is through a WebAPI 2 API
ASP.NET Identity http://www.asp.net/identity Handling user authentication
Autofac http://autofac.org/ Dependency injection
Entity Framework https://github.com/aspnet/EntityFramework EF6 is used for persisting the state of the domain model
Dapper https://code.google.com/p/dapper-dot-net/ Dapper is used for querying for data
FluentMigrator https://github.com/schambers/fluentmigrator/ Used for database migrations
Integrations
Postmark https://www.postmarkapp.com For reliable e-mail dispatching
Twilio https://www.twilio.com For text messages
Stripe https://www.stripe.com For handling subscriptions and payments
Sentry https://www.getsentry.com For error logging
Infrastructure
Azure Websites https://www.windowsazure.com For running the public facing website
Azure Web Roles - For running the multi-tenant web app
Azure Worker Roles - For doing background processing
Azure SQL - For storing all application data
Azure WebJobs - For checking that things are alive and healthy
Azure ServiceBus - For communication between roles
Testing
XUnit https://xunit.codeplex.com/ For unit tests
FakeItEasy https://github.com/FakeItEasy/FakeItEasy For faking and mocking
FluentAssertions http://www.fluentassertions.com/ For readable assertions in tests
Tools
GitHub https://www.github.com/ For code repository
TeamCity https://www.jetbrains.com/teamcity/ For continuous integration
Octopus Deploy https://octopusdeploy.com/ For automatic deployment

Thoughts about the stack

The first thing that comes to mind when I see the list above is that it requires a broad set of tools and techniques to create something, even if it is only a simple application.

The stack isn't exactly bleeding edge, the tools I have used are tried and tested. It has allowed me to move quicker during development, and to create something I can trust. It might have limited my opportunities to learn about exciting technology while working with the project, but that wasn't the purpose either, and some of learning efforts have gone into other areas like DDD and CQRS.

When it comes to ORMs I always see myself returning to Dapper. Yes, it does require hand coding your queries, but that also gives you a degree of control. I am confident I have spent much more time tracking down Entity Framework oddities than I have on keeping my Dapper SQL queries up to date.

I have used the TeamCity and Octopus combination since day one. I feel the reward for spending a few hours getting continuous delivery up and running is well worth it, even for smaller projects.

What happens next

I have a few must-dos on my list that I need to get out of the way. First of all, I have some trouble using my continuous delivery setup right now (related to updating a server in Europe while on a mobile broadband connection in the Philippines), I need to sort this out. I also have some work to do on the scheduling functionality which really isn't as good as I want it to be. I would also like to trim the frontend stuff a bit, it is slightly heavy as it is now.

With that out of the way I think it's time to start adding a proper external API, look into creating a mobile application and expanding with a couple of new features.

View Comments