In the last couple of months, I spent a lot of time studying Proophessor Do demo project that showcases features of Prooph components, all with the aim of mastering CQRS/Event Sourcing concepts. Along the way, something else turned my attention away from the main topic - unconventional, but clean and concise naming convention for class and method names.
This was a true eye-opener for me, I immediately liked the idea and after adapting it a bit I started practicing it at work. Excited and full of enthusiasm, I shared my findings and opinions with the rest of the world:
Inspired by naming style promoted by Prooph (https://t.co/XRb4boWTJQ), I decided to adopt similar style for my code. In short:
— Nikola Poša (@nikolaposa) December 24, 2018
- no `get` prefix for getters in entities and value objects
- no `Exception` suffix
- no `Interface` suffix
Result: tidy, less verbose, concise code. 👌🏻 pic.twitter.com/812cCkmMOr
Later I found out that Doctrine Coding Standard project is also enforcing many of these conventions which gave me confidence that I'm going in the right direction because parts of the community have already adopted such naming style.
I feel like I only scratched the surface with my tweet that opened many questions, without detailed explanation and reasoning behind the ideas presented. This post aims to fix that.
In order to understand what is good about this approach, we first need to understand what is wrong with the conventional one. Following are several naming conventions which I formulated as naming anti-patterns for reason. They are the motivation for designing a new, better naming convention.
Anti-pattern 1: Prefixes/suffixes convention for Interfaces
Let's start with the one for which there should be the least controversy. To give you better a sense of the problem, I'll make an analogy with one feature of human language - tautology:
In literary criticism and rhetoric, a tautology is a statement which repeats the same idea, using near-synonymous morphemes, words, or phrases, that is, "saying the same thing twice"
Tautologies are common in everyday language, and when unintentional, they are often considered a fault of style. Think of terms such as "round circle", "dry desert", "new innovation". Sometimes even acronyms are not spared of this: "ATM machine", "GPS system", "ISBN number".
Now consider this piece of code and read aloud the entire definition of this interface:
interface TodoRepositoryInterface
{
}
Exactly, tautology is present in programming, too. Until recently, I did not pay attention to that, I did not think about this at all. Now I realize that this imposed naming convention is as silly as using TodoClass
instead of Todo
for the name of the entity class. Here's why I believe so:
- Interfaces already have the keyword
interface
in their definition, and therefore usingI
as a prefix orInterface
as a suffix is a tautology that does not provide any additional value, but only blurs the actual purpose. - Each of these meaningless prefix/suffix naming conventions violates the DRY software development principle as well.
- UML class diagrams provide mechanisms to represent and distinguish classes, interfaces («interface» preceding the name) and abstract classes (italicized):
- Modern IDEs are doing a similar thing by visualizing files:
The same arguments apply to abstract classes and traits, and respective Abstract
and Trait
prefixes/suffixes. However, since abstract classes should never be part of any public facing interface (consumers will deal with interfaces, value objects, entities), I think that Abstract
prefix is somewhat acceptable exception to the rule. Of course, you could still come up with a better name by using alternative prefix such as Base
. Yet, this should be used as a last resort, because I assure you that you can get the right name if you precisely describe purpose and scope of the abstract class. For example, PdoRepository
could be an abstract class with logic common for PDO-based repositories, while MySqlTodoRepository
could be a concrete implementation.
Something that is not so common in PHP, and I hope it never will be, is practice of using Impl
as a suffix for concrete implementations. That is even more noise, more tautology, because anything that isn't an interface is basically an implementation, so putting Impl
suffix on every name of every class is absurd to say the least.
It is quite clear that language construct prefixes and suffixes do not bring any value, and they add nothing but more stuff to type to your code.
Anti-pattern 2: Archetype suffix convention for domain classes
What I'm focusing on here are classes that make up the domain, core business logic of the application - Entities, Value Objects, Exceptions, Events, and similar. Entities and Value Object are the least controversial, because we are all more or less used to choose a good name for them: User
, EmailAddress
, Todo
, TodoText
. But let's examine following Exception class that represents an exceptional condition in our domain:
namespace My\Todo\Exception;
final class CannotReopenTodoException extends \Exception
{
}
Reading it, Exception
suffix is not nearly as striking as in the case of Interface
suffix. Things start to change when you look into FQCN: \My\Todo\Exception\CannotReopenTodoException
because Exception
is already a namespace. But even if we ignore the repetition within the FQCN, CannotReopenTodoException
it is still a form of tautology, because wording and the context in which it is used (throw
, catch
, $ex
variable name) unambiguously indicate that we are dealing with an Exception:
try {
throw CannotReopenTodoException::notDone($todo);
} catch (CannotReopenTodoException $ex) {
}
It should now be clear that the suffix here is superfluous.
Event is another concept that is an indispensable part of event-driven and event-sourced architectures, and the same principle applies for them and most of the other domain elements that resides in their own namespace. The importance of the wording is even more pronounced in their case. FooEvent
does require suffix for clarification, but accurate description told in the past tense such as TodoWasMarkedAsDone
is self-evident.
Suffix removal is possible only if the class names are descriptive enough. Bad name is usually the result of a design flaw, and the inability to rename a class to something meaningful suggests that it has a poor cohesion.
Anti-pattern 3: "get" prefix convention for property accessors
Enough with tautologies. The claim that get
prefix is excessive is a bold statement, and it seems to be the one that is very hard to swallow for a lot of people who have read and discussed my tweet.
Getters and setters are the main forms of interaction with the properties of an object through its public interface. But given that Value objects are immutable, and Entities foster encapsulation, there should be no classical setter methods in any of them. In such circumstances, the getters take on the role of simple property accessors:
final class Todo
{
public function getId(): TodoId
{
}
public function getDescription(): string
{
}
public function getStatus(): Status
{
}
}
It becomes obvious that the get
prefix becomes excessive and provides no value to the consumer other than grouping all the property accessors with a common prefix.
I believe that the alternative "strip naked" naming convention for property accessor methods is a step closer to what will soon be a standard way of writing entities and value objects. Since the Typed Properties RFC has been accepted and feature will be available in PHP 7.4, and hopefully Read-only Properties RFC will go equally well, in the near future we will be able write our classes as follows:
final class Todo
{
public TodoId readonly $id;
public string readonly $description;
public Status readonly $status;
}
Final thoughts
I regret that it took me a long time to start thinking pragmatically about naming, because now most of my projects, both private and open source, still suffer from this tautology syndrome and the majority will never be healed. But going forward, every new work and new backward incompatible versions of my libraries shall be in accordance with the naming style elaborated in this article.
Comments