Skip to content

Instantly share code, notes, and snippets.

@domenic
Created June 6, 2012 22:18
Show Gist options
  • Save domenic/2885176 to your computer and use it in GitHub Desktop.
Save domenic/2885176 to your computer and use it in GitHub Desktop.
DDDNYC talk notes
"use strict";
// Essentials of prototypes:
var carProto = {
drive: function () {
console.log("driving a " + this.color + " car");
}
};
function carFactory(color) {
var newCarInstance = Object.create(carProto);
newCarInstance.color = color;
return newCarInstance;
}
var car = carFactory("blue");
car.drive();
expect(car.color).to.equal("blue");
expect(Object.getPrototypeOf(car)).to.equal(carProto);
// Sugar:
function Car(color) {
this.color = color;
}
Car.prototype.drive = function () {
console.log("driving a " + this.color + " car");
};
var car = new Car("blue");
expect(Object.getPrototypeOf(car)).to.equal(Car.prototype);
// Inheritance:
function Truck(numberOfWheels, color) {
Car.call(this, color);
this.numberOfWheels = numberOfWheels;
}
Truck.prototype = Object.create(Car.prototype);
Truck.prototype.constructor = Truck; // basically a bugfix
Truck.prototype.drive = function () {
console.log("driving a " + this.color + " truck with " + this.numberOfWheels + " wheels");
};
var truck = new Truck(18, "red");
truck.drive(); // driving a truck
expect(Object.getPrototypeOf(truck)).to.equal(Truck.prototype);
expect(Object.getPrototypeOf(Object.getPrototypeOf(truck))).to.equal(Car.prototype);
// ---
// Closure pattern:
function Car(color) {
this.drive = function () {
console.log("driving a " + color + " car");
};
}
var car = new Car("blue");
expect(car.color).to.be.undefined;
function Truck(numberOfWheels, color) {
Car.call(this, color);
this.drive = function () {
console.log("driving a " + color + " truck with " + numberOfWheels + " wheels");
};
};

Intro

  • How many people code JS? Informs how much time I should spend showing exact patterns, instead of telling you they're possible.
  • My experience: 170K LOC eReader application for BN.com.
  • A lot of the concerns are the same as in any language. So, much of it comes down to what twisted tricks are necessary.
  • Set the stage. Modern JS:
    • Uses modules
    • Uses packages
    • "use strict";
  • Encapsulation and coupling.
    • Encapsulation goes against the JavaScript grain, generally.
    • You can do private variables, but e.g. protected is impossible, and it has performance implications.
    • No notion of "internal" modules.
    • Much ends up being enforced by convention: people don't touch properties with underscores, and don't reach into your package.

What's Up with Classes in JS?

  • Classes do not exist. Classical inheritance does not apply.
  • An object can be a prototype for another object. "Fallback" concept. Methods go here; data stays on instances. Then the methods get called with a this pointing to the instance.
  • But classes are still damn useful as a domain-modeling concept:
    • Essentially, it becomes an issue of using factories, to produce objects with the same shape every time. And there's some (confusing) sugar for that, involving the new keyword.
    • You can recreate the notion of classical inheritance by first creating an object of one canonical shape ("class"), then create a second object, then use the first as the prototype of the second. If this process is encapsulated in a factory you get a "derived class" factory.
  • Bad news: this prototypal way of doing classes, is entirely incompatible with encapsulation.
    • There is an alternative, the closure pattern, which may very well be a good fit for your problem space. That is what we used.
    • It involves reattaching methods to every object instance, so that they can access an instance's private state, instead of putting them on the prototype shared among all instances.

Building Blocks

Unchanged

Services, repositories much the same.

Common practice is to make them singletons, since you can mock their dependencies without much work. But, this is not necessarily great convention.

Modules

= packages. Often correlated with git repos.

Entities and value objects

Equality semantics

  • There is no standard .equals method; no IEquatable<T> interface; no operator overloading :(.
  • In practice, this is not a huge issue:
    • I rarely need to compare value object equality, except in tests.
    • I compare entities by their IDs, e.g. product.id === product.id, or use repositories to tightly control their creation so they are reference-identical as well.
    • It's really more about the semantics, anyway.

Value objects

Object.freeze

Aggregates

  • Encapsulation issues rear their head here.
  • Factories are highly important to maintain it, if you're going to try.

Layered Architecture

More later

Supple Design

  • Side-effect-free Functions
  • Intention-revealing interfaces
  • Assertions
  • Standalone classes
  • Conceptual contours

Other

  • Domain events
  • Event sourcing
  • Document databases, JSON, no ORMs
  • Culture of BDD and integration testing
  • Bounded contexts: keep them! Don't use JavaScript's flexible nature to blend the lines. Enforce the boundaries!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment