17 Sep 2017, 19:56

Let's Talk Some More About Cars

There’s been a lot of great feedback on my last post about “Domain Driven Design.”

Here’s the first bit I got from the golang-nuts group:

Examples of what you call “factories” are right there in the go-to documents for Go beginners.

It’s absolutely true. It’s something I’ve been doing ever since I first started writing Go, because of those beginner documents. You can find more info on that here: https://golang.org/doc/effective_go.html#composite_literals

In general, a lot of what I wrote about has been stuff I’ve picked up in one form or another as I’ve gone honing my programming skills, and to be fair, “Domain Driven Design” has a lot more to it than what I wrote up in that post. What’s valuable is the way all the concepts come together, not just the ones I wrote about.

Another bit came from a conversation with u/DenzelM on r/golang.

The Car struct could be better. It should look like this:

// Car is the root object of our aggregate.
type Car struct {
	id string
	Color string
	wheels []wheel
}

// wheel is within our aggregate, so we don't let anybody
// access it from the outside. Each wheel must be accessed
// through a car.
type wheel struct {
	Diameter int
	Color string
}

// Wheels exposes the Car's wheels, while keeping the field
// private, ensuring the invariant can never be broken.
func (c Car) Wheels() []wheel {
    return c.wheels
}

This way, whatever business logic ties together a Car and its wheels can’t be undone.

This does mean that if you wish to expose the wheels in JSON, you would have to implement the appropriate interfaces yourself. This is because since the fields are private, the json package can never see them, so they will always be (un)marshaled into their zero values.

Here’s what “Effective Go” has to say about getters and setters:

There’s nothing wrong with providing getters and setters yourself, and it’s often appropriate to do so …

So thanks to everybody who left feedback. It’s been great and has helped me learn even more.

18 Aug 2017, 00:23

Domain Driven Design and Go

Go is notoriously tricky when it comes to software design because of the amount of freedom it gives you.

Recently I’ve been reading “Domain Driven Design” by Eric Evans. It’s been a breath of fresh air.

Here are some of the concepts in Domain Driven Design and how they can be used to help guide design decisions when writing Go programs.

Aggregates

Aggregates are just logical boundaries around different associated types.

The important thing about an aggregate is that anything inside the boundary is only accessible from a value of a specific type, called the root object. All other types within the aggregate must be accessed through the root object.

This makes a package’s API much easier to reason about. Instead of directly dealing with all its different types and their respective associations, the consumer only has to worry about one or a few types. This makes it easier for the person writing the code consuming the package to think about the business logic instead of the nitty gritty details.

package auto

// Car is the root object of our aggregate.
type Car struct {
	ID string
	Color string
	Wheels []wheel
}

// wheel is within our aggregate, so we don't let anybody
// access it from the outside. Each wheel must be accessed
// through a car.
type wheel struct {
	Diameter int
	Color string
}

In the above example, all a consumer has to worry about is a whole Car. This leaves the consumer free to deal with business logic related to Cars and their Wheels as a whole, without having to worry about the logic in between the Cars and their Wheels.

Factories

Factories are how you create new values of aggregate types. They are responsible for creating an aggregate value in a valid default state.

package auto

// types declared up here

// NewCar is a factory that returns a Car, initialized in a useful new state.
func NewCar(color, wheelColor string, wheelDiameter int) Car {
	return Car{
		ID: guid.New(),
		Color: color,
		Wheels: []wheel{
			wheel{Diameter: wheelDiameter, Color: wheelColor},
			wheel{Diameter: wheelDiameter, Color: wheelColor},
			wheel{Diameter: wheelDiameter, Color: wheelColor},
			wheel{Diameter: wheelDiameter, Color: wheelColor},
		},
	}
}

Here, the factory returns a completely initialized Car object, with all the other types in the aggregate initialized and tied to it. It would be invalid for a Car to be created without wheels, so the factory function handles this for us.

The alternative to this would be for the client code to tie the Car and its Wheels together. This leads so something like the following.

package auto

type Car struct {
	ID string
	Color string
	Wheels []Wheel
}

type Wheel struct {
	Diameter int
	Color string
}

// ========
package main

import "auto"

func main() {
    car := auto.Car{
        ID: guid.New(),
		Color: color,
		Wheels: []auto.Wheel{
			auto.Wheel{Diameter: wheelDiameter, Color: wheelColor},
			auto.Wheel{Diameter: wheelDiameter, Color: wheelColor},
			auto.Wheel{Diameter: wheelDiameter, Color: wheelColor},
			auto.Wheel{Diameter: wheelDiameter, Color: wheelColor},
		},
    }
}

This is messier for a couple of reasons.

First, the API exposed by the auto package is bigger than it has to be. To quote Rob Pike, less API is always a good thing.

Second, main now has to worry about Wheels and how they associate to a Car. This is a trivial example, but there could be other business logic around this, like the maximum diameter of a Wheel in relation to a specific model of Car.

We could just add the factory function to the auto package in this example as well, but now the consumer still has the option of getting Wheels by themselves and circumventing the business logic that goes into associating Wheels with a Car.

Repositories

The idea of the repository is that it abstracts away all the technical details about the database. As far as the consuming code is concerned, the objects are all in memory.

The key thing about a repository is that it only operates on an aggregate’s root object. In our Car example, the Wheel type doesn’t get a repository. Everything to do with Wheels in the database is handled internally by the Car repository itself.

This keeps the client code free from having to worry about tying associations together. Instead, it can focus on the business logic.

Repositories are easily implemented in Go using interfaces.

Here’s an example Car repository.

type CarStore interface {
	GetCar(id string) (Car, error)
	CreateCar(Car) (Car, error)
	UpdateCar(Car) (Car, error)
	DeleteCar(id string) (Car, error)
}

This is great for testing.

In the tests for the auto package, an implementation of CarStore can be created that’s backed by a map of some kind. All the objects could be held in memory, making testing easier by removing an external dependency and eliminating the performance hit of a network request to the database.

The auto package can have an implementation backed by a real database as well, for use in the actual application.

In both cases, the consuming code (whether tests or actual application code) only sees an abstract representation of the object store. It doesn’t actually know where those objects are stored, nor should it care.

Packages

Packages are probably the hardest thing to organize in any Go project. It’s very easy to get into situations where you have circular dependencies without taking the time to think about how to organize the code.

Fortunately, Domain Driven Design offers some tips on organizing packages.

Packages should be organized around specific concepts in the problem’s domain. Each package should contain one or more highly cohesive aggregates. Different packages should have aggregates that are very loosely coupled to each other.

For example, in our auto example, if we were dealing with a domain of vehicles, we could have an auto package for automobiles like cars and trucks. We could also have a watercraft package, for things like boats, that are very different than automobiles even though they are part of the same domain.

20 Aug 2016, 07:50

Error is for everyone

Writing Go has really taught me good manners in regards to error handling. Idiomatic Go handles errors right as they happen.

This got me thinking today. I was working on an AngularJS app and I thought it would be great to just pass errors.New onto the next stage of a Promise. It was then that I realized I could actually define a custom error type for use in my application, just like I could in Go.

The spec defines Error objects.

Instances of Error objects are thrown as exceptions when runtime errors occur. The Error objects may also serve as base objects for user-defined exception classes.

The key bit is that last sentence, which allows us to define our own Error objects.

// App Error is used for handling errors specific to our
// problem domain.
function appError(msg) {
    const err = Error(msg);

    err.name = "App Error"

    return err;
}

Since I’m working with Angular, I created a factory for my custom Error and called it appError.

function factory {
    function appError(msg) {
        const err = Error(msg);

        err.name = "App Error";

        return err;
    }

    return appError;
}

angular.module('myApp', []).factory('appError', factory);

Now I can include the appError factory wherever I need it and have a custom error type for my app. I find this especially useful when combined with $log. I use $log to send logs to Loggly, and I can see my custom App Errors right in the console.

11 Aug 2016, 08:43

meet quasimodo

Quasimodo is a little tool I wrote for easy publishing of Hugo sites to S3.

As joshuaboelter mentioned on reddit, the AWS CLI has the sync command that allows for easy uploads to S3. The sync command is great. I’ve used it before.

In terms of effort, I don’t think it was significantly harder than it would have been to just script the CLI. I wrote the first version pretty quickly. The CLI version of Quasimodo isn’t exactly a one liner either, because he checks to see if the bucket is properly configured to host a static site.

Also, when I wrote Quasimodo, I had in the back of my mind the scenario where I switch from S3 to something else. At that point, I could just give him subcommands for every provider and keep using the same program. To me, Quasimodo could publish Hugo sites to anywhere.

Anyways, that’s Quasimodo. Check him out on github.