Rationale

Tomer Gabel edited this page Dec 13, 2013 · 4 revisions

As with many open source projects, Accord started as an itch to scratch. At Wix we have a very large codebase dating back several years and originally built in Java on top of Spring and Spring MVC.

Since the decision was made to switch to Scala in late 2012, a significant number of new services were built on top of the internal development framework, and all of them had one thing in common: multiple RPC endpoints which send domain models back and forth. As more and more services were built with Scala, the DTOs themselves changed from bean-style Java classes with JSR 303 annotations to Scala case classes, nullable properties became Options and Scala collections were being used more and more heavily. With increasing productivity comes increasing complexity, and we quickly ran into severe limitations in the JSR 303 validation model. Most significantly:

  • Because JSR 303 encodes constraints as annotations, it's next to impossible to compose these rules; common JSR 303 constraints simply provide for most common use cases, but only in Java. For example, @Size applies to strings, arrays and Java collections, but it cannot be adapted to an Option[String]. A nice theoretical syntax would be @NonEmpty(@Size(max=5)), but annotations cannot take other annotations as parameters, so in practice we had to resort to implementing our own validators in a flat hierarchy, such as @OptionalNonEmptyString.
  • As with any model providing extra functionality over domain models in Java (serialization, ORM), the JSR 303 annotations add significant clutter the domain model. Because defining DTOs in Java is so verbose to begin with, what with having to manually define a field/getter/setter set for each property and implement toString and hash-code/equality, this isn't altogether a big deal; with Scala and its very concise case class syntax this can and does become a real issue. With one- or two-line DTO definitions it's also very practical to collapse all DTOs relating to a specific domain into a single source file, which makes the clutter even worse. Finally, because case classes automatically generate field/getter/setters for each constructor parameter, it becomes necessary to qualify the JSR 303 annotation scope with convoluted syntax like @(Size(max=7) @field) zipCode: String.
  • Implementing JSR 303 validators is a tremendous pain in the ass, requiring:
    • A Java class implementing the annotation @interface (because annotations defined in Scala aren't visible at runtime) with lots of boilerplate;
    • A class implementing ConstraintValidator using a limited, mutable domain to encode violation constraints.

While working around these limitations we begun searching for a cleaner, more powerful model for expressing validations. Being a Scala shop we had a couple more options to choose from, but we were unhappy with all of our options:

  • Scalaz's validation features are extremely poorly documented (as is Scalaz as a whole), and in practice relies on a bunch of examples and the occasional blog post to explain how it's used. Furthermore, constraints are typically encoded with function composition followed by a .failNel or .successNel call, which makes them hard to read and reason about; worse still, validators are combined with the IMO unbearable "Macaulay Culkin" operator (|@|) and provide a very poor violation model.
  • The InputValidator library provides a much cleaner model than Scalaz, but requires mapping your domain models to named inputs in the validator. While very flexible, with even a trivial number of validators this quickly becomes terribly verbose, and is also less inefficient at runtime.
  • Rob Dickens's idea for using Eithers to encode the results does away with Scalaz, but suffers from many of the same drawbacks: a hard to read rule definition model, and an overly simplified violation model.

Because we couldn't find what we were looking for, and because it seemed like an interesting challenge, we decided to write our own. Enter: Accord.