I had an interesting discussion with my co-workers this morning about architecture.
We’re putting together a sample web application to demo the default architecture we expect from applications. The idea is that applications should either be written to use the default architecture, or they should be migrating in that direction. As we have new ideas about how things should be done, we can fork the demo application and try our new idea out. Then we can brownbag our new work to the team and share idea. If the team likes it, the pull request will be accepted into the demo project.
I was asked to start the demo application work. I am a proponent of the Onion Architecture which clearly discriminates responsibilities among layers and enforces the rule that dependencies only point inward, and only at the layer inside the current layer. In other words, dependency depth for any class should be 0 or 1.
In Draw Abstractions From Concretes I wrote:
Waiting until you have an actual need for the abstraction proves that the additional complexity you’re adding is actually necessary and that the abstraction you’re creating is the correct one.
I’m basically just invoking YAGNI with a special focus on how you know “when you need it.” The problem is that in the demo application I’m creating contracts and services for each layer of the application even though the application itself doesn’t have enough functionality (yet) to demonstrate the need for those layers.
Our team is split.
One side believes that YAGNI precludes us from creating an Application Services layer when the Domain Services are sufficient to solve the immediate problem. The specific issue is that Application Services have their own contracts for inputs and outputs that look much like the contracts used by the Domain Services layer. Why not just expose the Domain Services directly? This just creates more complexity than is necessary to solve the problem and makes the code harder to understand.
The other side (me included) believes that choosing an architecture and sticking to it overrides YAGNI. It’s better to have an defined architecture applied consistently even in cases where it’s not strictly necessary from a purely functional perspective. The purpose of having an Application Services layer apart from the Domain Services layer is that it allows them to change independently over time. It’s immaterial that in the beginning much of the behavior is basically pass-through. Anyone who is going to work on the code base will have to learn what Onion architecture is (or whatever architectural pattern is chosen for the application) and the additional complexity will make sense.
What are your thoughts?