Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
A few days ago, I joined a discussion on twitter, that started with a statement, that you should not expose your entities as a REST endpoint except you are creating an application for a stage demo. The reason why I joined that discussion was, that I completely disagreed with that statement and still do! I write this post to explain why I do not agree with this statement and any other statements that are like always do this or never do that.
Don’t follow strict rules
The main reason why you should NOT follow these kinds of statements is, that they completely ignore the specific problem that should be solved by a specific piece of software. Solving problems or pain points for the user is the main reason why we implement software (OK and sometimes because it’s fun). If we want to do that in an efficient way, we should use all the options that we have. And that are a lot, if we do not limit them ourselves by ignoring a huge part of them.
I am often wondering why people think you should solve every problem in the same way. The main reasons I have read and heard so far are:
- Unification of the code base provides guidance to the developers and makes it easier to switch between different parts of a project or even between hole projects.
- Applying patterns and best practices helps to build high quality software.
As you might guess, I do not agree with that. Applying always the same structure or technology to implement a new service or use case can really help you to get started with new implementations or to find your way around in a new project. But technology or code structure are normally not the main issue, if you are new to a project. Business requirements, workflows and how the user thinks are the difficult thinks to understand. And these are not addressed by these kinds of unification. To make it even worse, unification often hides the real intention of the code because the developer forced the logic into a specific structure or technology.
The advocates of the current microservice hype take this into account and proclaim that you should select one specific technology stack for each service. I am not sure, if this is always a good idea or if it is a little too extreme. But it might trigger the required process to think about the requirements and how they can be solved in the best way. That can be the same way you used in all the other projects or something completely new, as long as the decision is based on the requirements.
Software architects often like to justify a uniform code structure or technology choice for all parts of an application by using existing patterns or best practices. This is completely against the idea of best practices and patterns and does more harm than it provides benefits. Don’t get me wrong, applying patterns and best practices are a good idea and can bring huge benefits. But each of them was defined for a specific kind of problem. There is no pattern or architecture to rule them all! So please, before you apply a pattern or architecture, make sure to read about it and understand its intention. Using a pattern under the wrong conditions can create lots of problems.
How to do it
The most applications consist of more or less 3 different parts that provide very different challenges:
- simple CRUD services that provide nearly no challenges at all,
- complex business logic and
- data oriented reporting with complex queries.
Should you handle these 3 parts in the same way? No, there is no reason for it! Understand the challenges of each part and choose technologies and patterns that help you to solve them!
Here is how I like to approach the 3 parts, but keep in mind, that these are only general recommendations. You might have different requirements that require different choices.
Productivity is the most import thing, if you need to implement a simple (and often boring) CRUD service. So keep it as simple as possible. There is no need to apply a multi-tier architecture for your business tier or to use some fancy patterns, if you only need to store some data in the database. In this case it can be perfectly fine to expose an entity as a REST endpoint and add some bean validation annotations to it. Just make sure that you have a direct mapping between the JSON input and your entity and that you want to apply the same validation rules on both of them. If you need to support a different data structure or a different set of validation rules, value objects might be the better approach.
The business logic part of your application contains the most source code and should also provide the most freedom to implement a good solution. Depending on the specific requirements you can expose entities or specific value objects or both as REST endpoints and apply the validation that is required to them. Do whatever helps you to provide a good solution. Applying patterns to solve the requirements often provides the most benefits in this part of the application. But again, it is very important that the applied pattern fits to your requirements. So take your time to understand the requirements and learn about the pattern.
In general, the database or whatever datastore you use is the best part of your application to handle complex queries. Therefore you should use all the features that it can offer to query and prepare the data for your reporting use cases and keep the business tier as small as possible. If that means that you cannot use some features of the frameworks you use in your business tier, e.g. you have to use native SQL queries instead of JPQL, than do it! You introduced the database to your system architecture because it is especially good at handling data. Use it like that!
OK, that has become way more text than I wanted to write on this topic. The main thing you should remember is, that there are no strict rules in software development that you need to follow every time. Each and every use case is different and you should adapt your solutions to it. That does not mean that you should ignore rules, patterns or best practices in general but you need to think before you apply them. Otherwise you will create an overly complex or very messy application and both will have a negative effect on your productivity and application performance.