Recently I published a post about Go application design called Standard Package Layout and I received great feedback. One common complaint, however, was that I didn’t have a public repository to showcase this design style.
So I thought that the best way to show this design approach wasn’t simply to link to an already built project but to iteratively build one and explain each step. I’ve done several times while mentoring but I’ve never tried it publicly so this will be an adventure for all of us.
Next I needed a simple but fun project to tackle. Fortunately, Peter Bourgon tweeted an ingenious idea for a WTF Dial. WTF is a WTF Dial? Essentially it is a real-time API for allowing members of a team to set their current “WTF level” and have it aggregated to a dashboard. It provides insight into the team’s health and allows users to easily provide their input.
Hopefully Peter has not raised VC money for this idea because that’s the project I decided to use for this series: https://github.com/benbjohnson/wtf
The idea is simple — start from the basics and build each layer iteratively. I’ll add these layers as pull requests and make line comments as needed. I’ll also provide a blog post explaining the design decisions.
Please comment on the pull requests and ask questions. I won’t be accepting pull requests for features because this is meant to be a learning experience. Do let me know if you have suggestions for features! I’ll try to work it into this blog series.
For this first post in the series, you can follow along with the first pull request about the domain model.
The domain model
The first step when I start an application is to determine the domain model. This is the set of data types and services that defines the language of the application. We can talk about how everything works in terms of the domain and it does not depend on the underlying implementation.
We’ll start with a simple set of domain data types:
- User — an authenticated user on the system
- Gauge — an aggregation of WTF levels from a collection of users
- UserInput — current WTF level of a user for a gauge
The term, gauge, is used rather than “dial” because the dial is the actual device used to input or display the aggregate value. Gauge is used in statsd to refer to a value that can be incremented and decremented.
The gauge will have an identifier, a name, and a current value. The value is between 0% (no WTFs) and 100% (OMG WTF) and is determined by aggregating the inputs but from the point-of-view of the API it is just a value.
Our domain data types don’t do anything. They just hold data. To persist them or retrieve them we need to have a service. Our GaugeService is what does this. I’ll implement this using BoltDB but it could be backed by any data store — Postgres, a JSON file, etc. The point is that we just need an interface which defines a contract between the caller and the service.
We’re going to support 4 actions to start with:
- Gauge() — retrieves a gauge by id
- CreateGauge() — creates a new gauge
- DeleteGauge() — removes a gauge
- SetUserInput() — sets the current WTF level for a user
It’s the job of the implementation to determine how all this works. All we need to define right now are the function signatures.
We also need a way to authenticate individual users. Normally this is done using sign up & log in pages but we’re lazy so we’re going to use GitHub as our authentication mechanism. Specifically, we’ll use the personal access token initially so that we can make simple command line tools without worrying about the intricacies of OAuth.
During testing we’ll use mocks to isolate our dependencies. Our mocks are going to implement the service interface but provide a way to set the actual function being called. They’ll also mark the function as invoked which is important when our function doesn’t return data for us to verify.
We’ll get into more detail about mocks once we start writing implementations and tests.
In the next post we’ll start at the lowest layer — the storage layer — and build the GaugeService implementation for BoltDB. I’ll get into serialization and data access patterns using a key/value store. I may also add alternate implementations for a relational database in the future.
Later we’ll build on that and provide an HTTP API as well as command line tools for sending data and displaying our dashboard.
Please find me on Twitter as @benbjohnson if you have any questions or suggestions!