Curious about the future of data-driven systems? Join our Data Engineering roundtable and learn how to build scalable data platforms.
Data Engineering: The industry has come a long way from organizing unstructured data to adopting today's modern data pipelines. See how.
Senior Consultant at MATHEMA Software GmbH
Paid to do Java Enterprise development since 1999. International speaker, teacher, trainer, consultant, architect and developer in software projects large and small. Passionate nerd, user and writer of alternative software libraries. @robertbrautigam
Stats
| Reputation: | 10589 |
| Pageviews: | 391.1K |
| Articles: | 5 |
| Comments: | 234 |
Comments
Oct 17, 2022 · Tomas Fernandez
What do you mean by that? Microservices _should_ have their own frontend! Unless there are very good reasons / requirements to the contrary, like supporting fully customer/user-built frontends.
Barring that, there is simply no logical reason to develop a microservice as pure backend and then a frontend separately. The two will almost always change together. You're not going to be "autonomous" if you have to coordinate with the "frontend" project. Also what about cross-functional teams? Continuous deployment? You can do none of these if the frontend is a different project / team.
I mean your 3rd sentence kind-of hints at this. The frontend development requires a high degree of interdependence. So why do it separately if it is obviously that strongly coupled?
What I'm saying, is that the "backend" and "frontend" boxes should not exist. A microservice is an _application_. With everything it needs to deliver business value.
Sep 29, 2022 · Shai Almog
I agree with you in general. Java is still popular and rightly so. It is much better than many people give it credit for and the JVM itself is pretty cool technology, with some awesome features. However...
There are problems. For example, there is a reason Java didn't have native syntax for setters and getters. It is antithetical to object-orientation, which in turn is a paradigm for building more maintainable software. Regressing now to having C structs natively supported, while popular, is a mistake. It _will_ result in even more unmaintainable designs. Yes, Lombok is worse, but the point is you shouldn't have the need.
I agree also, that checked exceptions are actually a good thing. But, they are _broken_. Since lambdas don't support them, there is really no way to use them properly.
Java _does_ have a cultural problem too. I wouldn't call it over-engineering, I would call it _bad_ engineering. JEE is a symptom, but it exists almost everywhere. Every project that starts with: let's do hexagonal architecture, or layered architecture is guilty of this.
Annotations contribute to bad designs very much. Runtime annotations are workarounds for not finding a proper way of doing something with code. Sure, they are easy to write, but they are much worse to read, follow, understand or maintain in general.
So... I agree with you with those caveats :)
Sep 14, 2022 · Otavio Santana
I appreciate your articles, always good insights into what's going on! Let me however disagree with the following:
The problem with Java Persistence is not technical at all. It is the notion that persistence needs to be disconnected from the business case. In trivial cases, like mostly CRUD cases, this sort-of works, but breaks down pretty quickly in anything more.
Because these tools push you to have a persistence "layer" that is mostly disconnected from actual use-cases, it simply does not have the knowledge to determine what happened to the data. It has heuristics to figure out how to best persist some "object graph", with some technical controls given to the user in the form of annotations mostly. Which is both completely unnecessary if you retain the context of the use-case, and positively harmful to the codebase.
The very notion of persisting "object-graphs" is already pretty shaky at best.
Aug 29, 2022 · marcytillman25
A good summary, but I feel these things are *way* overblown.
If you need data from another service, or need to "data mutate" in a coordinated fashion between services, especially if you control most of these anyway, you're probably already doing something wrong.
Microservices are supposed to be small, functionally closed, self-contained applications, with ideally *no* request-response communication need.
I'm not saying you *never* need the tools you list. But in all honesty, I think these sorts of articles are actually harmful. People will think they *need* these things to be "cool", when in fact it's highly likely they're just sitting on a wrong architecture.
Recommending GraphQL under the chapter of "essential" is particularly troubling. Microservices are not database tables. Treating them as such leads exactly to these kinds of tools used as over-engineered workarounds for a (probably) wrong approach.
Aug 23, 2022 · marcytillman25
This is a completely wrong approach to the wrong problem.
There were no such things as "data services". When microservices came along, people just started doing CRUD services (as you rightly pointed out actually) for data. That was not the idea, nor does it make any sense. If you want data, use a database.
Domain driven design is _not_ a practice to identify "data types". It has nothing to do with data, but with thinking and writing in business language and following business-related behavior as closely as possible.
Your suggestion is to double down on an already bad idea and gather/aggregate data through additional infrastructure.
All these problems disappear if the services actually contain their business functions and all data to fulfill their function. Unless you have very particular requirements, there is no point in asking for data. Just design the service to _have_ that data.
Aug 17, 2022 · Nicolas Fränkel
This is wrong thinking. Microservices are not there to return data, that's the database. Microservices are small applications, they should own a business function.
If they don't return data, but enable functions, suddenly all those issues you describe are not that big of a deal. The third diagram, with lines going everywhere is _exactly_ how the "web" works. It is the only system so far able to scale, that we know of. So to say this architecture becomes impossible to maintain when it becomes large is incorrect. Obviously the "how" is important.
In all honesty, you're right. Most people understand microservices to be basically database tables with CRUD functionality. If that is what microservices are, then what you wrote makes complete sense!
They shouldn't be though.
Aug 03, 2022 · Otavio Santana
Great article. However I disagree with the notion that OOP and "data-oriented programming" (let's face it: procedural programming) can co-exist.
They can co-exist the same way side-effects and functional programming can co-exists. I.e. not at all.
The problem at its core is that database records should not be represented in code. The impedance mismatch should be corrected by reading data into real objects with behavior instead of pseudo-objects which are glorified C (or COBOL?) data structures.
This also logically means that the whole discussion around JPA is moot, since JPA is a completely wrong approach.
I also think Sonar should flag all usages of Java records as an immediate code/design smell.
I have to note, that the reasons for the above are completely pragmatic. We tried procedural programming. It doesn't scale. It's becomes a mess. Most projects today become a mess exactly because they don't follow through on OOP.
Jul 28, 2022 · Ankit Dixit
I may have missed something here, but JSP vs Servlet?
I mean old doesn't always mean bad, right? But even back in the day when we were sold this thing, it was not a good technology by any means. It was supposed to be a quick and dirty answer to ASP.
JSPs by their nature can only really work by extracting data from somewhere (beans), and then have logic in them to create the UI. It basically forces objects to violate encapsulation and then needs to have some of their logic too to work. They create a highly coupled, very fragile, non-compile-time safe, parallel universe to code. They're bad.
There is no reason at all to use JSPs and every reason to use other, saner libraries, whether server-side, client-side or both.
Yes, JSPs are still technically part of Java/Jakarta Enterprise, just like EJBs still are. Still, you shouldn't use either of those things, even if you decide to use Java/Jakarta Enterprise for some reason.
Jul 01, 2022 · Otavio Santana
Encapsulation is not about "security". It is not even about maintaining class invariants (like not adding nulls, like in your example).
It is about controlling knowledge, abstraction and expressiveness.
In other words, we don't hide "the data" because we want to prevent wrong access, we hide it because having it available would by definition smear *knowledge* about that data to many places. We don't want that, since that would mean changes will not be localized if that piece of knowledge / semantics / business changes.
Getters _always_, by definition, violate encapsulation. _Always_. It is any easy test: Can/Do outside objects access the data? Yes = encapsulation violated, No = Encapsulation intact. It doesn't matter how, whether it is through direct field access, getter, or even reflection.
In _very_ rare occasions this is a necessary trade-off, sure. But it is nonetheless a violation.
May 11, 2022 · Periasamy Irisappan
I remember reading a similar article 15 years or so ago about SOAP/UDDI/BPMN. Do you think it is different this time?
Apr 27, 2022 · marcytillman25
I assume with "REST" you mean Remote Procedure Calls through HTTP that result in a JSON data structure, usually directly from a database table. Essentially SQL through HTTP/JSON.
So GraphQL is a replacement for SQL, right? Not REST.
Apr 07, 2022 · Veronica Xu
Cryptographic algorithms / primitives are selected based on requirements, based on threat-model analysis mostly. It's not about "advantages" and "disadvantages". There are no such things in general. Everything is dependent on use-case, so such a trivialized analysis is not useful.
Also MD5 is not an encryption algorithm. It is a message-digest algorithm, i.e. it hashes. Also, it's considered BROKEN. Don't use it.
Especially don't use it to "store passwords". Also don't use SHA for that. Not by itself. Use a proper password hashing function designed specifically for this use-case if you have to do it yourself. See: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
Mar 03, 2022 · Murali K
Did we though? :)
Mar 01, 2022 · Dmitry Egorov
Great idea to take another look at these things. However, I do disagree with almost all points to varying degrees.
1. SRP. The last try from Bob Martin to define it is "should be responsible to one and only one actor". So if this class is used by one actor, it's fine. Does it have one responsibility? Sure. It's responsible for Singleton stuff.
2. OCP. Having read the Parnas paper, I don't think having a private constructor violates OCP. Neither does having "final" classes. Not subclassing things is actually a good thing. Subclassing is hard and should be the absolute last resort if all other tools fail.
3. Liskov only applies to subclassing, so if there is not subclassing, you're fine. Again, no possibility to subclass does not violate Liskov. Liskov simply does not apply.
4. ISP. Does not apply. I agree here.
5. DIP. I sort-of agree with you, but not for the reasons stated. Depending on "abstractions" does not mean to depend on physically abstract classes or interfaces, although those are good things. It means to depend on things on higher abstraction levels in the design. That could be a concrete class too actually. This is sometimes violated with singletons, so I agree with that.
Also you don't have to immediately jump to an IOC Container. Simply passing the same instance of an object does the same trick. Your point could be presented much more clearly if you just use hand-written initialization code where the same instance of something is passed into multiple other things.
Again, I agree with your overall point though, that having one instance of something is not the problem. Only how you actually do it might, and static singletons are a pretty bad way to do it.
Feb 17, 2022 · nick flewitt
Emphatic NO.
Making data private and then having public getters and/or setters on that data is NOT encapsulation. It is exactly the opposite of encapsulation.
Just think about it. Do external objects have access to your data? If yes, that data is not encapsulated. It is that easy. It is completely irrelevant whether that access is through direct field access, methods that return them, or even reflection.
Why is this important? If others have access to your data, you don't control the data. If you don't control your data, you can't reliably change, extend, remove things without impacting others. I.e. it is less maintainable.
Again, it is not just about your own internal state, it is about the data itself.
Feb 11, 2022 · Tyler Hawkins
Good article. In my experience writing and maintaining documentation is nearly impossible though. Outside of process-heavy industries like healthcare I mean.
It's much more cost-effective to just spread the knowledge to multiple people. Keep as little documentation as possible and keep everything live, either in brains of multiple people, or in code.
Try to actively eliminate the need to know stuff implicitly. Difficult to setup? Make the build spit out a docker images or vm images and/or have k8s scripts. Difficult to setup an account? Automate or bundle into the app / image. Must support multiple browsers? Create some tests that'll catch you eventually if/when you forget.
Good documentation is difficult and expensive and should be therefore last resort.
Dec 23, 2021 · Micah Zayner
Your comment is highly ironic on many levels.
REST (the original one, the one in Fielding's dissertation) was explicitly made with "orchestration" in mind. This is what the often misunderstood concept of "hypertext as the engine of application state" means. It means the client does not orchestrate. The server does. The server is in full control of the workflow. What can happen, when and in which order, where, how, etc.
So then projects decide, no we don't need that, that is too complicated. Couple of years later it turns out that makes the whole "API" brittle. Because now the clients are basically hardcoded to the server, causing all sorts of maintenance problems.
Then people come up with all sorts of indirections to solve this, instead of doing it right. API Gateways, GraphQL, Swagger/OpenAPI, etc.
GraphQL is more or less typed JDBC. If you're building a database, you might find it useful, otherwise there's no point.
Dec 20, 2021 · Paul Parkinson
Excuse my bluntness, this is all just hype. This whole technology stack has minimal actual value.
Why would you even have cross-boundary transactions? Why would you want atomicity across microservices? These are all strong indicators of a bad architecture.
You're showing how to still have 2 tables with ACID, while technically complying with the current hype words. Don't get me wrong, I'm not blaming you personally :), just pointing out what I see.
The linked comparison between Kafka and Oracle is deeply flawed. You make up an architecture, specifically one that would require distributed transactions, and then complain that there are no distributed transactions. The actual scalable solution is to design your solution right. And if you don't need scalability, sure, you can use a relational DB.
What I'm getting at I guess is that I would need a much better example, where better solutions do not exist.
Nov 05, 2021 · Bartłomiej Żyliński
I'm not quite sure I understand. It's the same people. The user logs in, sees her/his balance and then decides to transfer some funds (or not). It's the same actor. Working with the same thing.
By "actor" do you mean people? I mean is "Joe" who has an account an actor in your definition?
Nov 04, 2021 · Bartłomiej Żyliński
So according to your description, my Account class, which has "void transfer(amount, target)", which transfers an amount to another account and, "Html display()", which displays the Account in HTML form conforms to SRP, right?
Both methods touch the same part of the domain. Check. It is responsible for only one actor. Check.
Nov 02, 2021 · Thomas Hansen
This is just flamebait, isn't it? But, it's also a strawman.
Robert Martin != OOP. Yes he said some clever things back in the day, but if you listen to him, he is not an OO fan. More importantly SOLID doesn't mean anything. (Except for Liskov). He tried 5 or so definitions for SRP already, none of them work. Everybody understands it however they want. Maybe that is its power.
What I mean is, you can rant all you want, but if you equate OOP with SOLID, you're just showing you don't know what you're talking about.
Oct 30, 2021 · Sergiy Yevtushenko
Excellent points and insights! Couldn't agree more on the "loss of context" argument and its connection to nulls, frameworks and magic.
By the same argument though, if nullable variables lead to context loss, "beans" and other pure data structures should be an order of magnitude worse, shouldn't they? With nullables you don't know whether they can be null or what that would mean, but with beans, you don't even know how they're used, where they're used, what dependencies exist between its "properties" if any. There's literally no context provided.
Oct 08, 2021 · Sergiy Yevtushenko
Definitely agree with the direction. Also not using 'null' should be the absolute minimum for any Java developer, functional or otherwise. I wrote about this before.
However I do disagree with rule #2. Using "Result", "Either", "Maybe" or such structures to represent failure leads to constant map() and flatMap()-ing, probably nested. I find it already syntactically confusing to use Optional and CompletableFuture, and those are usually pretty confined for now.
I wouldn't want to deal with monads manually (at that scale at least). Unless Java introduces a do-construct a'la Haskell or for-comprehensions a'la Scala I don't really want it.
And if we get there, there's the slippery slope to monad transformers, monad stacks, type-classes, higher kinds, etc. I'm not saying that would be bad, I'm just saying I don't see that happening in a mainstream language.
Aug 24, 2021 · Trinadh Chakravarthi
This article has absolutely _nothing_ to do with DDD, Bounded Contexts, Microservices or "API"s. You just briefly describe a typical layered architecture application. That's it. There's nothing new, layered architectures were done like this since forever, only the labels changed somewhat.
If you changed the title to "A typical layered architecture application", and removed the first three or so paragraphs, I'd be probably ok with the article.
Jul 21, 2021 · Bartłomiej Żyliński
So, if you're advocating for FP, sure, I'm with you. :) But Java is an imperative side-effectful language, and void has its place. In that context void is not bad, not even most of the time.
If you "forgot" to call some method, that's just bad api design, nothing to do with void.
Also, I don't see how it leaks implementation details, or how doesn't provide a contract. Again, if you are generally against side-effects, I'm with you, but if you're ok with them, this is an ok way to do them.
It is not harder to test either. What is maybe harder to test are side-effects, but again, you're not arguing against those, are you? All methods, even those with return values can have side-effects, so there is no difference in testing to void methods at all.
All in all, I think you're arguing against side-effects, and not "void". Maybe more examples would help to clarify?
May 20, 2021 · Lydia Defranchi
API Design Decision:
1. Use OpenAPI, which needs a lot of verbose configuration that are already defined elsewhere (like in the HTTP RFC) and hardcodes everything into to client, making upgrades virtually impossible. Then, use another tool to check that the verbose configuration even makes sense.
2. OR, just use hypermedia and define the media-types using plain language. Without any paths, methods, HTTP codes, and so forth.
There was a time, where you could have made the argument that RPC (Option 1) is just easier to do. The more tools you add, the less valid that argument becomes. Everything else is already much better with Option 2.
May 20, 2021 · Melissa Habit
I'm just wondering aloud. Are APIs really that important? Are they really making customers "come back"?
I mean APIs are used by developers. You could say APIs are from developers to developers. None of the decision makers really ever get to see the technical details. We (developers) use horrible APIs all the time, it's not really our decision to use them or not. Even if it costs 5x as much in implementation time to use as another API, that won't really matter.
So what am I getting at? Maybe the first question should be: Who will presumably decide to use this API? Developers or business people? That will ultimately decide what is important.
Apr 21, 2021 · Tetiana Paratsii
Thanks, it's a good read.
I have some issues with #7 however. Wouldn't providing annotations for the reviewer already mean that the code itself is not readable enough?
I probably wouldn't want information that is deemed necessary by the writer about the code to be in a review tool and not the code itself.
Apr 15, 2021 · danieloh30
Thanks, that explains it.
Apr 15, 2021 · danieloh30
I've read the article. I get that it helps you reduce startup time, but how does Quarkus help you reduce time spent on recompiling or redeploying?
Just for fun, I compared the example Quarkus projects to my own Vert.x-based projects.
Quarkus: Builds ~6s, starts ~0.050 sec
Vert.x: Builds ~3s, starts ~1.2 sec
So the actual turn-around time for development is still seem to be better with Vert.x for example.
Feb 06, 2021 · Otavio Santana
I went into reading this article with a smile on my face. Finally! There's a trend to end this reflection-based nonsense that's so prevalent in Java, and the article will probably show some alternatives how "normal" code can be just as easy as using annotations, but much more readable, traceable, etc.
Then it dawned on me. This article is just about removing the "physical act" of reflection and move it to compile time. It does not get rid of annotations that are actually hidden black-box behavior. It just makes the hidden black-box behavior _faster_.
Am I getting that right?
Jan 06, 2021 · Bassam Alugili
Clearly a lot of thought and work went into this, so a great achievement!
However, it is basically everything that's wrong with today's architecture and design.
It is perfectly procedural in all aspects, sheds all pretenses of being even remotely object-oriented. With it goes all best-practices of OO as well.
It is also very loosely connected to the domain, it is dominated by technical classes. (Data structures, Commands, Validators, Handlers, etc.) Just look at the packages and class names.
It is a maintenance nightmare. Basically any change anywhere requires changing multiple places. The UI probably is always involved. Changing commands, events, probably involves even more components. Changing order/products also multiple components. There is no abstraction nor encapsulation anywhere, therefore can't really safely change anything.
Dec 18, 2020 · Victor Rentea
I read your article, watched your talk too, and I feel you consistently do exactly the opposite of what you actually want.
Let's start here: "...touching the 'sacred' getter...may impact up to dozens of places". So basically things that change together are not together, right? You start your youtube talk with SRP, isn't that what SRP is about allegedly?
So then you mention "Entities" as bags of data and make a point of *not* including any logic in them. You even say that the Entities should not have anything to do with use-cases, because they "need to be generic". Why? This is exactly how you get to the problem above.
You say "KISS" is a principle. Well, the simplest solution is to *include* the formatting logic in the object. You don't want it there just because? Something MVC something?
The same with this article. Your goals are fine but your doing exactly the opposite of what would help you.
Dec 18, 2020 · BUHAKE SINDI
This article describes pretty well why CDI is fundamentally broken, even if that was not the intent.
So you have to qualify your beans. Why would a bean even know or care how the application uses it? Why would a "CreditCardPayment" or an "EmailNotification" define itself as "default"? This decision should not be made here, and neither on the injection side by the way, which has even less standing to decide what implementation it uses.
But it gets worse. Because CDI relies on reflection and not on normal instantiation and parameter passing, you sometimes have to "name" things. With strings. This unifies all the problems of the previous point with the sloppiness of a dynamic script language.
CDI is just a remake of an already bad idea. You don't have to use it, especially now, that it is not "official" Java.
Oct 31, 2020 · Brijesh Saxena
It is a fair description, but you really shouldn't use this anymore, especially in the form shown here. There are several problems with this, here are a few:
1. Inheritance is not for implementation convenience, it is for modelling a very specific property of the domain.
2. Superclass gives access to internals to subclasses. Inheritance does not mean suspension of encapsulation.
3. Setter/getters that nobody seem to use. Setters/getters are bad enough, but *unused* setters/getters are worse. :)
4. Temporal coupling between manufactureCar() and toString(). I.e. if you call the latter without the former it will give you nulls.
All in all, I would write huge disclaimers at the start of the article, like "do not use". Also I would try to find a much better example, something that is more realistic, with less anti-patterns.
Oct 23, 2020 · Ori Keren
It's a cool article, nice dashboard.
However, I still think the problem is partly with us, i.e. developers. You shouldn't have to "translate". Developers are there (well, should be there) to build the business, not to do some magic technical stuff.
Developers should speak the same language as the business, in addition to knowing technology. If they're not, how are they delivering value? How do they understand what is needed? On the flipside, as tech becomes part of the business, the business needs to have some understanding of it too.
I feel like having "metrics" and "kpis" for each other only serves to isolate the two, not to bring them together.
Oct 22, 2020 · Dmitry Egorov
I agree that "testing in isolation" is currently overvalued and some people even insist in deforming the actual design to conform to it.
However, there is still value in proper unit tests vs. an integration test. I disagree with the conclusion that we should categorically reject either of those.
Also, I'm not a native speaker, but I think you mean "strongly recommend" instead of "hardly recommend".
Oct 02, 2020 · Stuart Meikle
Excellent points made why rich models are to be preferred.
However, the article then completely misses the mark by talking about data. Data is invisible in a rich environment where *behavior* dominates. The behavior defines the data in an object, there is no "intrinsic" data there.
We're not just trying to limit setters to protect class invariants, it much more than that as you seem to describe at one point, but then forget about. If you have getters you are not exactly "rich" anymore, because getter are not "business" behavior.
Also, if you have getters you are not protecting your data anymore and the problems in the services you describe start to creep back. Logic starts to spread around about how to handle that raw data.
Aug 17, 2020 · Ralph Soika
Spring Boot and Jakarta EE is like a duplo / lego thing. You can quickly plug things together, it's colorful (literally, Spring Boot has ascii art) and easy. You can start relatively quickly.
As long as your goal is to build a play-house, they're fine. But if you want somebody to actually live in it, it probably won't work.
Your architecture should be defined by your requirements, not by a framework.
Aug 04, 2020 · Otavio Santana
That is just wrong for so many reasons. And I don't mean the article, or whether it works. I'm sure it does.
I mean the technology and overall style and design. This has nothing to do with REST, with object-orientation, based on the amount of annotations not even with Java. And the functionality is not even something you would need to implement, since all databases do CRUD for you.
Please at least come up with a compelling use-case where this design makes at least some sense. I have never seen a project where any of this makes sense, and I worked on microservices.
Jul 11, 2020 · Alireza Rahmani Khalili
Do you have code to show what you mean? Words don't really mean much in our profession:)
Jul 10, 2020 · Alireza Rahmani Khalili
You say that, but then you go on to describe the aggregate in terms of associations, object graph, updates cascading, transaction over data, acid, etc.
That is still a data model, even if it is not the one from the database.
All objects, including aggregates should be defined by their behavior, not technical details like relations to other objects, transactions. This is not just theoretical, it does result in completely different designs.
Jun 10, 2020 · Moe Long
Also, if you are developing, and looking for a good, lightweight, keyboard, CPU and screen-estate-friendly window manager, I recommend i3. :) Have fun!
May 26, 2020 · Javin Paul
My main problem with this list, is that most of it is too imprecise. Many of these, mainly the SOLID ones are so badly defined, that they are often used to justify exactly the opposite positions, like having pure data structures, and procedures / services.
I have a counter-proposal, with just 2 rules:
1. Hide data always. Have them private, don't have accessors on them.
2. All public names should be directly related to the business. Keep technology out of the names.
You can still have some of the other ones, but only after these 2.
May 21, 2020 · Vishal Chovatiya
Nice summary, though I disagree with your conclusion.
If you are just starting out, you are much better off just ignoring all this crap. At the very least ignore SOLID! There is so much misinformation or just plain wrong information out there, you are really just better off not listening to all that. That includes Uncle Bob also.
Even Design Patterns, if someone says: "I implemented this with pattern X", I would have to look into the code to see how they've done that and why, and whether it helps at all. Sure they are useful to get ideas, but they are much less useful than people think!
If you are really just starting out, concentrate doing OO right first. Just have objects that don't expose data (don't have getters and things are private), and are business-relevant. That's it. It took me couple of years to get even somewhat proficient at that.
May 07, 2020 · Valerii Sloboda
I get where that is coming from, data and functions are clearly separated in FP, so it seems on the surface that the DTO/Service approach is similar. But the two are very different, technically and conceptually too.
For example "Constructors" of data types, not always admittedly, are hidden. That means the outside world usually does not know what the data type is made of. I.e. there are no getters to the outside. Functions that operate on the type are bundled as a "module", or by other means that have access to the constructor, therefore the "properties". That is how encapsulation works, very similar to what an object would be.
Now true, that is not always the case. There are also typeclasses, which is basically how you can add polymorphic behavior to an existing data type. That can be done from the outside, so that's also something like services I guess. But this also can not always be done, only if the "business methods" of the data type allow it, or the constructor is public.
Of course you can write an unmaintainable mess in FP too, there is nothing really that is stopping you from choosing the wrong design.
May 06, 2020 · Valerii Sloboda
You're right, OO is changing, but FP is changing too, and both are converging strongly in my opinion.
FP started with strong theoretical foundations and gets more usable / practical / watered down.
OO started as usable / practical and imprecise and moves towards correctness and stronger language tools.
The underlying thinking is almost the same. Having encapsulation is a thing in FP too! Bringing things closer that change together. Polymorphism is a thing in FP too, although technically a bit different usually.
What is clear is that the currently popular Layered Design, Clean Architecture, DTOs, Beans, Anemic Models etc. is not supported by either paradigm. So regardless which you choose, things can't go on like they have.
May 06, 2020 · Valerii Sloboda
Adam your statement is so complex, it requires multiple points to properly rebut. :)
First, your strawman: No one is saying that OO is a sliver bullet. There are certain indications that it results in a more maintainable design than procedural programming.
Second, your anecdotal evidence: Even if true, you can not say how successful (i.e. maintainable) those systems would be when proper OO was used. You even said yourself you didn't see any examples of it, so you have no basis for comparison.
It is also an argumentum ad populum: Most people do it that way, so it must be good. Obviously no.
Apr 29, 2020 · Fabio Borriello
Spot on!
If you have a design that is based on data structures you already left object-orientation behind.
If you have multiple data structures that are basically the same and you have to automate copying between them, you left sanity behind.
Apr 27, 2020 · Mike Gates
Why are you creating your own instance of CompletableFuture when you get one anyway?
If there is no specific reason I would just thenApply / etc normally.
Apr 10, 2020 · Robert Brautigam
Thanks for commenting. Yes, in theory you can pass an object around that you got from a getter. That should be allowed.
The Law only triggers when you actually try to call a method on it. There are some grey areas of course. Because the object is now a parameter to the next method, in theory that method should be able to call methods on it. This is described in the original study as one possible strategy to get code compliant with the law.
Because this is a grey area, I would look at it case by case, to see if the semantics of those things allow one to know about the other. In general every time you have a getter, you'll probably violate the LoD at some point.
Apr 08, 2020 · Ravi Sharma
Maybe you have worked on well-designed systems most of your career, I don't know.
I haven't, even though all projects seem to follow strict layering, everyone read "clean code", etc.
Here is an exercise: Just look at a couple of commits on your "domain" and see whether only the domain changed in those commits.
I've done that for one of my current project. I can't seem to find any other than "sonar" commits (fixing typos, comments, minor technical stuff).
There is one commit that adds some stuff only to our "domain", but it actually breaks the "rest" interface implicitly, because of reflection. I assume there was a matching commit in the ui project as soon as they realized it's broken.
Apr 07, 2020 · Ravi Sharma
What you say all sounds good in theory. Sure, if you do everything right, changes should not propagate. I agree with you in what we want to achieve. My point is exactly that this type of architecture does not do it right.
A simple example: You have Accounts, and you want to transfer money out of the Account through a simple Web interface. Account is a domain object of course. It has a "transferTo(Account, Money)" method let's say. Money is also a domain object.
Sound great, until you notice that you have to be able to present both the Account and Money, and even worse, you have to get input from user and construct both of those.
Now what is your "public contract"?
Most projects of this type will say: Let's have the Account and Money be a data structure (a POJO, Bean, DTO, whatever). At that point your public contract is basically everything. Now which changes would not propagate out at this point?
Can you really change any of those things now in any meaningful way without changing the UI?
Apr 02, 2020 · Ravi Sharma
You didn't mention the tradeoffs that come with such a design.
As with Clean Architecture and other designs that try to create "pure" business logic, the disadvantage is that things that belong together are not together. Any change in the core has the potential to ripple through all your adapters.
Let me repeat that. Changes in the core (i.e. business changes, that you probably do all the time) will cause changes in potentially many dependent modules.
You generally don't want this, unless you have a very very very good reason.
Dec 18, 2019 · Attila Mihaly
I don't agree with your takeaways at all, but the underlying point you make is valid I think.
That is, equals() in Java is broken. It is because it is automatically defined for every object, which is just confusing and leads to all sorts of problems you point out. This is however not an implementation problem, but a conceptual one.
Not all objects have an easy to define equality. When are two Products equal? Well, depends on what you're looking for in your use-case. The only sensible solution is to avoid equals() (except for Collections).
Nov 07, 2019 · Avraam Piperidis
I consider most of this bad advice (not all admittedly). I used to think all this is just misunderstood as you said, but I came to believe it is just plain wrong.
For example SRP implies (or even explicitly says) that we need to split off technical aspects from an object, like you showed in your example. This is just wrong. An object should be a cohesive thing, with everything it needs to be authoritive on some subject. If that is producing JSON, or displaying it on a Web interface then so be it.
We should not be implementing "Services" and "Views" and "Models", we should implement business-relevant objects like "Vehicle" or "Car", etc. The former implies that we focus on the technology, the latter that we consider our business-requirements more important.
If there is no business-relevant reason or technological constraint that forces us, things that belong togther should stay together.
Nov 01, 2019 · Lindsay Burk
I think you're right, maybe I'm being a little unfairly dismissive.
I'll keep this in the back of my head, and see whether I encounter any use-cases for this in the next couple of projects. If not, I reserve the right to be dismissive again :)
Nov 01, 2019 · Lindsay Burk
Thanks for the reply, I think I understand. That is my problem though, I don't think what you describe is a valid use-case in most situations. (As always there are some rare exceptions)
To me a "service" is something that offers meaningful functions to do something. A "database" is something where I can get data. Databases are usually "hidden" behind services and are not generally available directly to other services nor clients.
GraphQL looks to me like it is about getting data, so it is a database technology. But databases should be hidden under a meaningful service. If that is the case, then the Service could aggregate multiple datasources itself and wouldn't need a separate server with GraphQL.
To be honest, I think I know what's going on. I think projects don't hide data under meaninful behavior as they should, that's why this is so popular. The many "REST with Spring" articles we see here are a testament to people just implementing database facades all the time without actual logic. This all leads to just having distributed monoliths, where everybody just has access to all the data it wants and doing whatever it wants with it. (As I said, with some rare exceptions)
Nov 01, 2019 · Robert Brautigam
Putting it in terms of dots will not work unfortunately.
If you see something like param.b().c(), it is not immediately clear whether that's allowed. If b() returns a new object, instantiated during the call, this is allowed. If b() returns an object which already existed prior to the call it is not allowed.
This is all because of rule 4. If you want a short (but very slightly incorrect) version of LoD here it is: Don't have getters.
Oct 30, 2019 · Lindsay Burk
Thanks for your reply, and for getting the irony. :)
I would think that if you have to aggregate across multiple databases or services, you are already doing something wrong. The services/databases are split wrong or the use-cases were not clear. Unless it involves things that you (as a company for example) don't control, in which case you are not responsible for the design of those obviously.
Even then, I would perhaps try to create a semantic facade and offer meaningful services for the external things instead of pure data, which just smears the interpretation through all of the clients and makes updates much harder.
I'm still not sure what use-case it is aimed at. Perhaps in a heavy legacy environment where better designs are just not available?
Oct 30, 2019 · Lindsay Burk
If you need a service where you can specify what data you receive through what relations, well, that is called a relational database. That is neither REST nor SOAP, that is a database.
There is a also an alternative DSL to these kinds of things called SQL. Granted, it's not JSON, but you at least don't have to write code for it to make it work.
Why is this a thing? I don't get it.
Oct 11, 2019 · Eliran David
You've made a good point about watching Thread usage when writing code.
However, I would suggest there is no reason whatsoever to block a thread when waiting for I/O. This is 2019 and most of the libraries, if not all, support non-blocking modes of operation.
The magic of CompletableFuture is not that it can push blocking operations to different threads, but that it enables writing non-blocking code.
Oct 09, 2019 · John Allen
Thank you for the longer reply, however I feel like you are not adressing the main points I've made. Regardless of what we think abstraction or cohesion should mean, my main point is that this architecture is unmaintainable.
Let's just consider you are a new developer on the team and want to find out how the Cart works, or have a ticket even that something in the Cart doesn't work. Where do you look? All the DTOs and Beans are just noise, they don't tell you anything except what data may be involved. You might look at the "use-cases" classes, but there can be a lot of those that involve the Cart.
You can't even easily identify which use-cases class might involve the Cart, because data may be copied through intermediaries that originated in "the Cart". Even if you do find a good spot for further investigation, you have no clue how the data there came to be. You have to back-track through all the layers and DTOs again to find where the data is actually set to find out what the data actually means and how it is potentially dependent on other data.
You can't really touch anything because things will break elsewhere. Try to add some feature to the Cart which needs some more data? How much work is it to go through all DTOs, mappings, requests, responses etc. to extend, or even understand where it needs extending. Then you have to go through, again, all use-cases trying to figure out whether this new feature will break something in exsiting code.
Everything has to know everything else. Every "gateway" has to know how the data looks in intimate detail to present it, to persist it, etc. These things will almost always change together but are scattered through the whole application.
And so on...
Oct 07, 2019 · John Allen
Don't get me wrong, this is an interesting topic, but I find myself disagreeing with every significant statement you've made. Examples:
Technically yes, but misleading. We are working with abstractions for a reason.
Input, Processing and Output of the same thing can be, and often is, very cohesive.
Where is the Cart? And I don't mean the data for the `Cart`, but the behavior. What it does and how it works? It's nowhere. It's spread around in who knows how many classes and packages.
This architecture is a maintenance nightmare. Let's add some feature to the `Cart`. Bummer, there's no such thing (it's a data struct). Now I have to parse through all Use-Cases to see how Cart is used and where to put my feature. Add a new field to Items. Well, let's again read thourgh all classes where this field might be important to use. Etc.
I mean, just look at the class and method names. How many of those would be recognizable to subject matter experts, or dare I say speakers of the Ubiquitous Language?
Oct 04, 2019 · Nam Ha Minh
There's no denying that it is indeed filling a need.
The question remains however: Is this need justified? And I think this question is much more important.
Oct 03, 2019 · Robert Brautigam
Sure, but right now we are not adhering to it at all. DTOs, Beans, and other non-objects, which almost always lead to LoD violations are rampant in every project I see. So talking about trade-offs is a bit disingenuous at this point.
LoD is a little bit like Kung-Fu. You have to start with obeying it religiously in letter and spirit. After you've done a project or two that way and became a master, you've earned the right to go your own way if you so choose, not before.
Oct 01, 2019 · Nam Ha Minh
Encapsulation is not enforced through setters getters, in fact it is violated by them.
Encapsulation is not about validating data in a setter or making defensive copies of stuff in a getter. It is about not having the knowledge of the internals of an object on the outside. It somewhat overlaps with the concepts of data hiding and abstraction.
Sep 19, 2019 · John Vester
Field injection just sweeps the problem under the rug, but doesn't really solve it.
The many dependencies and all the problems associated with them are still there.
Sep 19, 2019 · John Vester
In my experience too many dependencies are often a side-effect of procedural thinking and design.
Just like in your examples, when you think about "steps needed to be done", i.e. Services, there's not much abstraction that you can put on that, hence you will have one "controller" type thing that has to coordinate everything.
However, if you do a proper object-oriented design you can introduce much more levels of (business relevant!) abstraction to combat a complex problem, with no need of a "central" part that coordinates everybody.
So I would say yes, too many dependencies are probably a sign of sub-optimal design.
Aug 27, 2019 · Amanuel G. Shiferaw
No, no. Please don't have both options!
The result is always the same when there are multiple ways to do something: my project will have a mix of both, with usually exactly the wrong one at each occurrence.
Remember, we're not working alone.
Aug 27, 2019 · Amanuel G. Shiferaw
As Yannick said, the problem with Java's ?. would have been that the type-system would not have been aware of it. It would have been absolutely up to the developer to use it or not. I.e. it would have been just syntactic suger for an if statement, but would have made the code not any bit safer.
Kotlin does it much better because something that's nullable is not assignable to non-nullable types, so there your argument holds syntactically. Semantically however they are different. "Optional" is just a part of a better design resulting from not using 'null' values. There are Null-Objects, implementing behavior that semantically means that the object isn't there. There are callbacks (inversion of control) that just don't call if there's nothing. Etc.
My point is that 'null' encourages a bad, data-based design in addition to just causing NPEs. The Elvis operator just treats the symptom and does nothing to discourage or prohibit bad designs.
Aug 27, 2019 · Amanuel G. Shiferaw
The two solutions are the same. Both are bad.
If it's up to the developer to check, including the possibility of forgetting or messing it up, it's already bad.
Aug 26, 2019 · Amanuel G. Shiferaw
The problem with the "?." operator is that it solves the wrong problem, it's essentially a work-around.
The problem is not how to handle nulls, but to not have them in the first place. There is absolutely no reason whatsoever to ever return or accept null. That is where our solution should be, not littering our code with random null-checks just for "defensive programming". This is also the issue with your example, these classes are simply badly designed and should not exist.
This is where Kotlin missed a good opportunity to do it right (it introduced the "?." operator instead of solving the real problem). Unfortunately Java is heading in the same direction, there will be records soon, and I'm sure this topic will come up again.
Aug 20, 2019 · Tomasz Linkowski
Kotlin took a step in the right direction, but stopped short of actually solving the issue. As does JSR 305.
There is absolutely no reason whatsoever to accept or return null values, ever, so it shouldn't be possible at all.
It fulfills all of your requirements and it is the shortest, clearest and simplest solution. Anything other than that are just hacks or workarounds.
Aug 06, 2019 · Lindsay Burk
The big difference is where the control is. If you iterate yourself, you have control and the object you are iterating is reduced to a dumb structure. Furthermore you are required to define the technical details of the iteration, for example that it is a synchronous potentially blocking iteration (like the enhanced loop).
If you ask the object to iterate for you, the object itself has control. It can choose the most efficient way for you, it can asynchronously page the result, it can cache since it knows when you are ready, it may decide to not block you depending on the semantics, do any kind of neat tricks.
Jul 01, 2019 · Jordan Baker
You mix two separate concepts in your description, messaging choreography and messaging technology.
A request-response exchange will always wait for the response obviously, regardless of technology, and a fire-and-forget message will not.
So what combination do you define as "asynchronous"?
Jun 18, 2019 · Lindsay Burk
I like the realization in chapter "The Root of the Problem", that the approach is wrong, and you have to take a few steps back.
However I expected you to go in the direction of composition instead of inheritance, because this is pretty much the case for it. You don't need inheritance here (as far as I understood you), it is just for convenience to access some functionality that is common. Now F relies on some special semantics of A, as do all concrete subclasses.
Instead there concievably could be a single interface which all implement, and the final semantics is just a matter of plugging them together. In this case they woudln't have to depend on eachother.
Jun 05, 2019 · Lindsay Burk
I don't exactly know what your point is, but that statement is not entirely correct.
Technically you can write perfectly good asynchronous and non-blocking code using a single thread.
In practice that is done when dealing with I/O, where CompletableFuture is basically a mechanism to write continuations. Other threads only come in when there is actual long-running computation involved, which is just a smaller subset of problems when writing non-blocking code.
Jun 04, 2019 · Saurabh Dashora
I think I would have started the article with a paragraph like: You shouldn't use any of these things.
Instead of events, you should use normal method calls. Instead of CQRS you should have a proper behavior oriented object model. And you should definitely not have distributed transactions either physically nor semantically.
That being said, if you find that your application naturally starts to evolve any of these things, you just might have a problem which warrants some of these patterns. But it only counts if you weren't trying to get it to fit.
May 29, 2019 · Robert Witkowski
Can CQRS apply to any extent to a more object-oriented design?
This article, as all I've seen so far start from the design that data exists somewhere (often called the "model") and there are services which do stuff with the data. I.e. the data and functions on them are mostly separated. This is a popular approach of course.
What about designs where the function and data are mostly together? Is there some interpretation of CQRS that would apply there? The problem that I see is, there is no such thing as a "query" in these kinds of designs. The "Tell, don't ask" principle states it nicely that you mostly don't "query" objects, you tell them what to do.
Is there some article or video on this topic?
May 29, 2019 · Jordan Baker
Ideally you would want to make Microservice4 oblivious of what happens with the infrastructure.
Why would it do that? The inrastructure that launched M5 already knows everything. M5 should not assume nor care about what environment it runs in.
Why would M4 care about what instances there are? M4 should just ask the infrastructure (i.e. the network) to find a suitable instance, that's it.
I don't understand the need for Eureka and similar servers. There is already a technology called DNS, that has existed for 4 decades now, is reliable and solves this problem perfectly.
In addition there are load balancers, proxies and other completely transparent network elements with which arbitrary scaling and elasticity can be implemented without problems and without the Services knowing about any of this.
May 20, 2019 · Daniel Sagenschneider
You can't say "Kotlin for some OO" and then present a bunch of data classes without even so much as a single "token" method to be able to pretend they are real objects. Data classes and OO are as sharply divorced as Procedures and OO are.
Ultimately you are doing procedural programming as you seem to acknowledge yourself. You concentrate on technology (like threads, reactive stuff, transactions, etc.) and what technological steps would be taken (i.e request processing, validation, storage, etc.). That is pure procedural decomposition, procedural design, definitely not a mix.
My point is, this is less about combining paradigms, more about a (very opinionated) technological integration between different languages with different syntax.
May 03, 2019 · Lindsay Burk
I updated my code to use "join()" the same way your code does, so the Stream fails if a CompletableFuture completes exceptionally.
Regarding threads: the language of your article seems to strongly suggest that the number of CompletableFutures are somehow bound by the maximum number of threads possible. I merely suggest to make that text clearer, since that is not the case.
May 03, 2019 · Lindsay Burk
I have a different idea, which is just 5 lines long, requires no mapping and has no practical limitations, using an intermediary blocking queue. Gist here:
https://gist.github.com/robertbraeutigam/a0a06318bf953042ac61819e4ac79ed5
Also, not all CompletableFutures are waiting for a thread, that's only if you create it with one of the async() factory methods. It is perfectly possible to create 100.000s of CompletableFutures without a single thread.
Apr 26, 2019 · Otavio Santana
I like that you want to create a rich domain model, but you didn't succeed. Your model is still an anemic model.
A rich model is one where you have business methods and an anemic model is where you just give access to the data. Yes, there is one kind-of sort-of business method, which is the "goal()" method, but you generally just give access to data. It doesn't matter whether you validate, don't have setters, have a builder or create custom types for things. That is not what "rich" means or should mean.
To have a rich model, you have to have methods that do something, as in: solve some problem in your domain. Getting data doesn't solve anything, unless you're a database.
Mar 22, 2019 · Stefan Wolpers
I would like to have better definitions. There are at least two things we can talk about:
There is "technical debt", i.e. shortcuts that do not impact design/code quality and were consciously made, and there is plain old bad design/code.
The latter far outweights the former in every project I've seen and is caused by a lack of understanding and/or technical knowledge. Also, it is not measurable. SonarQube can't tell you that your model doesn't fit the requirements, or that you are leaking data and concepts all over the place, even though this has much more impact on quality than some unused imports or execution path inconsistencies.
The solution: Have at least 1-2 people who know what they are doing technically and are (or will be) also domain experts. Scrum does seem to put the focus on "people", so I guess it does sort-of address this.
Mar 05, 2019 · Daniel Sagenschneider
I've read the introduction and the first few patterns of your paper, skimmed the rest. Most of the things you talk about seem similar to EJBs (thread-pool, scaling, message dispatching, queueing, etc.), Actors (exception-handling, re-visiting tasks) and other frameworks. Sure, there are some very limited use-cases where these work, but they are far from generic architectural patterns.
Also I strongly disagree with the statement that object-orientation or functional programming enforce "top-down" "micromanaging" approach. I wanted to find out more of your ciritique of these, and you reference the Pandey paper. Which actually talks about the research in this area being not done properly (using students, difficulty of teaching/adapting to oo, etc.).
I guess what I want to say is: show me the code.
Not of the framework, but an actual business-application that is written with it. I guess that would make me understand it better. Do you have something like this?
Mar 05, 2019 · Daniel Sagenschneider
I do feel like I'm missing the point, but I do want to understand it.
The website that is being linked to seems like a web-framework with some unique philosophy about offices.
The "problems" listed here (ctor parameters vs. method parameters) and on the website (async, non-blocking stuff) have more conventional solutions also, so that's not really a big deal.
I feel like this is approaching the Inner-Platform Effect. It's abstracting over method calls and parameter passing in an attempt to be so generic and decoupled that it becomes virtually meaningless (==purely technical).
Mar 04, 2019 · Daniel Sagenschneider
I definitely applaud taking things to their logical conclusions. However I disagree with the premise, therefore with the definition you provide as well.
Coupling is not inherently bad. Yes, we want to minimize it, but all in context of modeling a problem in an object-oriented way. That means first and foremost things have to have a meaning. Objects exists because they are part of a solution, they model something from the problem domain. Their method names, parameters, return values reflect that.
To be calling a specific signature, while is technically coupling, is actually expressing some domain interaction (i.e. something real) that I do want to express.
IoC and DI are just tools to achieve a more perfect model, they do not exists for their own purely technical sake.
Feb 21, 2019 · Sibanjan Das
Is that ment to be ironic?
I would expect a good developer to produce the same code regardless of tools. If I give you a compiler and a text editor, you should be able to code just fine. Maybe a bit slower and with more cursing perhaps, but the quality of code should be exactly the same.
Feb 18, 2019 · Kiran Kumar
Can you explain the reasoning a bit more? Let's say two applications share a Customer table. I get that this is bad, since the two teams can not independently evolve.
So your solution is to replace the shared Customer table with a shared set of messages. How is that an improvement? Presumably those messages will also contain the same information and have to be agreed on the same way?
Even if they don't contain the same information, why not just create a shared table for the limited information set then? Then you wouldn't have all the eventual consistency and atomicity problems that you want to solve later in the article.
Feb 11, 2019 · Amit Phaltankar
You solved the problem:
> insert into dog values (...);
> delete from dog where ...;
> select * from dog where ...;
Just with a lot more time, effort, cpu and memory wasted. Please don't do this in real projects, at least not when it's internal anyway.
Feb 11, 2019 · Ales Nosek
Well, yes I do, thank you for the question. :)
Anything worth using is worth coding. Conversely anything you can generate, you can generalize.
To demonstrate my point, consider the databases that can generate input/output forms from their schema. I had to use some of those back in the day. They do work, and you can actually use them, but they are a pain for anything more than real trivial data entry. This is because they have zero business knowledge. You have to know how to input data, in what order, what everything means in relation to another, and what effect that will have on the system as a whole.
I have the same problem with autogenerated apis. I can input data, but there is zero knowledge about actual use-cases I want to accomplish.
Feb 08, 2019 · Akhil Raj
I think I understand this craze around API Gateways. They have a colorful UI, they look cool on architecture diagrams, and the features they list do seem reasonable on first glance.
I have yet to see a real use-case where they make sense though. Most features are covered by existing (completely transparent) network infrastructure software/hardware. Like proxies, caches, SSL termination points, DNS. Management in this day and age is easy and scritable, whether it is Ansible, or whole environments like Kubernetes.
Routing and delegation is a non-issue, especially if you use REST (I mean really, with links and forms).
Cross-functional and DevOps teams should strive to be directly visible anyway, not hidden under a level of indirection.
Jan 26, 2019 · Jordan Baker
Asynchronicity in itself is not good enough.
Properly written async / non-blocking code should: Never use their own thread-pool for I/O, and never block for any reason.
Asynchronicity is not a goal in itself. The real goal is not to waste reasources/threads. React to I/O on the threads the I/O library already has, this is both for request, database queries or queries to another service over HTTP.
You only have to create your own threads if there is some CPU intensive calculation you want to do.
Jan 23, 2019 · Duncan Brown
I know this is a technology demonstration, but I really wish the example would be closer to something real.
Your example will be taken litereally. There is already a lot of projects building "microservices" that are basically data CRUD endpoints. You are essentially writing database software at this point, instead of something useful to the business.
If you want data, use a database. Use JDBC or some other already existing protocol. There is absolutely no need for these kinds of "microservices" in a real project.
Jan 21, 2019 · Chandu Siva
I propose DZone should cap the amount of singleton pattern articles to at most 1 / year.
Even then, only publish, if the article starts with the sentence: You should never use this pattern.
On second thought, the article should also end right after that first sentence. :)
Jan 05, 2019 · Dave Taubler
Thank you for your reply and allow me to disagree again :)
Yes, there is data an every application. That does not mean that data needs to be published inside the application. Having "data" objects like DTOs or any sort of JavaBeans and "Managers", "Controllers" is the polar opposite of object-orientation. It is called procedural programming. This is the way we developed software in C, where we have structs for the data and procedures (functions with sideffects) for the behavior. Obviously this is the most familiar design for those of us transitioning from procedural langauges (it was Pascal for me actually), that is why it stuck around. It has little to do with OO though.
This is not forced on a developer. This is a design choice. You can choose to think of software as data and operations separately, or choose to think of it as "things" working together each with its own behavior. Your design will obviously reflect your thinking, but both is equally possible.
Just try it, start a project without any data objects. Consider it a Kata or something. Implement some game, like chess or tic-tac-toe without getters or setters for example. "Step out of your comfort zone" :) I'm available for comments and discussions if you want.
Jan 04, 2019 · Dave Taubler
Strongly disagree with your intro. Properties are the antithesis of encapsulation. The whole point of properties is to make data available. The point of encapsulation is to "capsule" data, i.e. hide it behind a barrier.
This is not a syntactic issue either. It is not about making data immutable, copying it or validating it. It is about not publishing the data in the first place. I suspect your gut told you this when it told you that you can just as well make data public instead of making setters getters. :)
You are supposed to offer the proper behavior that uses the data, behavior that is business-relevant. That is what object-orientation means.
Dec 21, 2018 · Robert Brautigam
Thanks for noticing. It was already corrected on my blog, unfotunately I have no way of correcting it here. Bugs me a bit.
Dec 18, 2018 · Robert Brautigam
If I would write a database library I would not model the NULL database value as null in Java. It wouldn't make sense, and it would be actually wrong, since the database's 3vl differs from the semantic in Java. For example NULL != NULL in relational databases but null == null in Java.
For your second point. I don't really argue either way about object internals in this article. Fields / properties whatever are private to an object. What I care about is the public interface, i.e. public methods.Dec 18, 2018 · Robert Brautigam
You are talking about 3rd party libraries like database and xml parsing libraries. Sure, you'll need to check for null there because they are mostly badly designed (with nulls as valid values).
A proper database or xml library would never ever return null, so you wouldn't have a source for nulls. That is exactly what I mean, that the problem is code that treats nulls a valid values. We have to quarantine those libraries, create a null-free barrier, and not pepper our whole codebase with null-checks.
Dec 18, 2018 · Robert Brautigam
I'm not arguing against all those, at least not in this article.:)
However I should not be required to inform any client that nulls are invalid. Nulls are invalid by their very nature, I'm arguing for acknowledging this fact. Adding an annotation is redundant and does not inform the client of anything new.
Dec 18, 2018 · Robert Brautigam
Yes, I get that. You are talking about NPEs in production that happen out of nowhere.
Those things happen because nulls are legal values in some parts of the code. There are beans which can have null values, service calls which accept nulls as some default etc.
If you don't allow any of that, and test your code even moderately, those things become extremely unlikely.
Dec 11, 2018 · Robert Brautigam
Would there be any public method parameter or return value that you would not annotate with @NotNull?
Dec 04, 2018 · Robert Brautigam
My point exactly! Object.equals() is one of those methods that do accept null, and it forces us to do stupid and redundant things.
This argument shouldn't be in favor of keeping null-checks, it should be an argument for getting rid of methods that accept nulls, so that we don't need null-checks in the first place.
But I agree with you, that when we're interfacing with code that does not respect these same values, we are probably forced to null-check sometimes. However, I would only do that for return values from 3rd parties, not for parameters for my own methods.
Dec 04, 2018 · Robert Brautigam
If there would be an annotation for that, would there be any public method that you write, where you would not use this annotation on all parameters?
Dec 04, 2018 · Robert Brautigam
Why wouldn't there be an NPE?
The only reason would be that you are not dereferencing your parameter (directly or indirectly). I.e. you are not using it. This is only the case if you "handle" the null in some way, or null is a legal value of some kind.
I argue in the article that all of these cases themselves should be illegal. The result is that NPE will be thrown. No magic necessary, only correct usage of parameters.
Nov 16, 2018 · Lindsay Burk
Functionally this code works, but you create a lot of threads that will wait for another I/O operation to finish. You basically throw away resources for literally nothing, and you might be even creating a deadlock because of mismatching pool sizes.
What you should do is to use non-blocking operations. I.e. don't wait on any thread, ever. Waiting should happen in the kernel's "select()" operation, and the processing in the I/O library's thread pool. No need to create a new one.
Nov 08, 2018 · Lindsay Burk
It does make sense, and I know it's a lot of work to write an article like this, but let me add two more points:
1. I think "real" code, with business logic behaves and looks differently than technical code, like CRUD persistence.
2. Examples matter. I've seen projects firsthand where there are "layers" of code like this (by that I mean generic technical boilerplate code that has nothing to do with the "business"). Or just look at Stackoverflow with all the "DDD" questions that have tons of code like this. So it may be a showcase for you, but there are people who see it and then bring it into real projects.
I would just like to ask you to bring code that you would write in a real project, so it creates no confusion about best-practices. I know it's more even work that way, but I think it's worth it.
Nov 08, 2018 · Lindsay Burk
What you've built is an HTTP frontend for a relational database table. We already have databases, most of them already can expose data not just over JDBC but over an HTTP-based protocol. I can get a "REST" api from my postgresql without a single line of code for example.
So your tools are optimized for something people shouldn't build in the first place.
On a constructive note, I would like to see examples of "real" code, where classes and methods have business meaning.
Oct 25, 2018 · Duncan Brown
I would like to question the importance of swapping out components.
Sure, it sounds good, and it sometimes does happen, but the real question is: how often? How often do you swap out a web UI for a console one? How often do you swap out a database to be a service? Etc.
The incurred cost of this separation is huge. In your example you introduce "data objects" to carry data across boundaries, thereby causing hard coupling all over the place. Not a good thing for most use-cases.
Sep 26, 2018 · Mladen Bolic
You are using code coverage wrong.
You are not supposed to "achieve" code coverage. Unless you have management forcing everybody to treat code coverage as a "KPI", code coverage should be a tool for you to find potentially untested code, spot code that fell through the cracks.
You seem to have it completely backwards.
Aug 25, 2018 · Brijesh Saxena
Technically the article is correct, but I have issues with the example.
First, don't call classes "Strategy". Objects are still supposed to model the business, not display your "pattern skills". Call it simply "Interest", "CompoundInterest", etc.
Be clear about what belongs to whom. Look at the "rate" information. Seems only the "Interest" uses it, so why do _all_ the objects have to know about it?
I might also argue, that "rate" is not a parameter for a method in "Interest", but a state, i.e. a constructor argument, something fundamental to an Interest.
Why are things mutable? Why are there setters/getters? There is no reason for them, so they should not be there.
Does the "Interest" depend on the account type or not? Decide which one, and design accordingly.
"Double" as balance might be ok for a specific context, but others here might be not so forgiving. :)
Aug 23, 2018 · Duncan Brown
Oh, I didn't mean that as criticism, I was just curious how it works.
Your library looks cool. One of my pet-issues with CompletableFuture was/is that allOf() returns a completablefuture of Void. I honestly don't know why they did that. Why not return the list of results?
I see that you've fixed that with Promises.all().
Aug 23, 2018 · Duncan Brown
Cool feature. I was interested how it's implemented and looked at the source code.
It turns out it uses an internal singleton (static) instance of a scheduled executor thread to schedule all timeouts.
Aug 09, 2018 · Manuel Rivero
Cool article. Which one is the Connascence of "Knowledge"? You know, when the caller has to know something the callee already knows.
Like having a "Money" class that has getters for "Currency" and "Value", now the caller has to know how to interpret them.
Or when an object has an "isSomething()" method and based on that the other methods might return something slightly different?
Aug 03, 2018 · Brijesh Saxena
I thought we had this already.
Don't do singletons, and don't do the "monostate" things either.
There is, as you mentioned, simply too many things that are wrong with it, and it is pretty easy to avoid. If you want an object to exist only once, instantiate it once and pass that single instance around! No need for anything static.
Jul 26, 2018 · Javin Paul
Those are certainly good enough answers for a developer, even too much lexical knowledge for my taste.
However, for a lead or architect I would expect a much clearer distinction between REST and HTTP. I know, I know, I don't want to debate what REST is or isn't. But somebody in higher technical position should know both the original meaning (from Roy Fielding's dissertation) and the colloquial meaning.
Jul 04, 2018 · Nikesh Pathak
Ok, so we disagree on what REST and RESTful HTTP should be. That is fine, let's try to look at all this pragmatically, without relying on what defintions we use:
What is achieved by making such a service? What problems does it solve? Does it decouple, encapsulate anything? Does it make the architecture simpler, easier to grasp?
We can quite clearly see that it does not solve any business problems (check the github repo), there is no business logic whatsoever anywhere to be found. It is a pure technical service, it is a glorified database table, or object store if you like.
Does it solve an architectural problem then? Do we need to scale the Customer independently? I doubt it. Can we deploy it independently? I doubt it, since it just data, and everyone is tightly coupled to this data format. Does it make the architecture simpler? No, it infact complicates it, and introduces a synchronous network roundrip for no discernible reason.
The problem I am having with this, is that I see this approach in real projects. Teams doing "services" for customer, orders, documents, tickets, which are all basically database tables. Some tutorials on the net even suggest splitting a monolith based on the database diagram. It just makes everything worse, and it taints the concepts of REST and Microservices.
Jul 03, 2018 · Nikesh Pathak
The point of REST, specifically in the context of Microservices is to offer functionality by providing representations of application state (hence the acronym REST).
It doesn't just provide data, it provides the client's state and all the options the client can choose from to progress in the application! The client does not implement a Use-Case based on the data, it chooses an already implemented Use-Case from the server-provided options coupled with the data.
Even if you don't provide options (i.e. don't use hypermedia) you could at least provide meaningful services instead of the raw data. I don't really see the point of providing CRUD access to raw data from an architectural point of view. You can just as well make a JDBC connection directly to the database, it wouldn't really make any real difference for an internal application.
Jul 03, 2018 · Nikesh Pathak
I know this is just a tech demo, but please don't do this in a real application.
1. No business logic only pure data.
2. API is just CRUD directly into DB.
3. Architecture purely technical, no meaning whatsoever.
4. Technical (meaningless) package structure.
5. Everything hard-coupled to data object.
6. Data Objects.
7. Meaningless, superfluous interfaces.
Jun 26, 2018 · Marcos Barbero
Thanks for taking the time to answer.
I understand my objections are not exactly about the topic of your article. However examples matter. Most people, and I say this with some experience, will take your example literally and will do it exactly like you showed.
I get that your focus was to show some technical possibilities with Spring. But I would like to suggest, that you to always show things you want to show through examples that are also otherwise best practices in all other areas of discourse. This is sometimes not easy to do, but it will reduce the likelihood of creating problems if taken literally.
Basically: if you wouldn't write it in a project, then don't have it as an example, regardless of topic.
Jun 26, 2018 · Marcos Barbero
Some comments:
1. Do not use Optional as parameter. Construct either with or without the service, but not with Optional.
2. Do not use isPresent(), get() from Optional. Use ifPresent(), or other methods.
3. In your last example, the class should be independent of the DI framework. Either pass the dependency or not, don't do a lookup.
Jun 02, 2018 · Héctor Valls
I don't see why.
I would argue you have to expose all services separately, otherwise you can't really be independent.
If you are not independent changes tend to propagate instead of being localized.
Which in turn limits the velocity of change.
Jun 02, 2018 · Héctor Valls
If your goal is to develop distributed, independent, autonomous services, having an "API Gateway" is an anti-pattern!
Use hypermedia, or even redundant data if you need to, but most projects should not need an "API Gateway".
May 23, 2018 · Chanaka Fernando
I would like to push back on the notion, that any additional central infrastructure (like API Gateways, or specialized Routing Services) is necessary for a microservices architecture. They are not.
A good microservices architecture would seek to minimize central dependencies, instead of introducing them. Sometimes they might be ok, but they're definitely not necessary!
May 17, 2018 · DZone_karap
In the first diagram comparing Monolith and Microservices, you have a "UI Service". In the last paragraphs however you state there should not be a separate UI Service.
Also, an "API Gateway" is not part of Microservices. It is actually an (anti-)pattern that is used as a substitute for hypermedia.
May 17, 2018 · Alejandro Duarte
I get your point, but "redeploy" does not mean "rebuild". Redeploying in a different environment does not break any principles of microservices.
If you change any settings of the environment is it basically a new environment. The environment itself should be version controlled / reproducible at all times.
Changing settings on a running production system dynamically (without version control, on a GUI for example) should not be allowed anyway, I hope we agree there. And supplying "environmental variables" to applications is not a new thing, and can be done, well, with "environment variables" easily.
Again, you did not bring any use-case where a central server is needed which the application itself must know and ask for settings.
The application should get every envrionment-specific settings through environment variables. It's the reason those exist. Those can come from a central server of the infrastructure, but that should be completely transparent to the application. The application should not be required to make synchronous requests just to start.
May 16, 2018 · Alejandro Duarte
Let's stop at "why do we need this". You say bundling the config would require redeployment of possibly hundreds of instances.
Well, if you have hundreds of instances, you probably automated their deployment. A rolling redeployment should be a (reproducible!) push of a button at this point. So why not do that, instead of yet another synchronous runtime dependency?
Apr 05, 2018 · Emmanouil Gkatziouras
These examples are strange and partially smell:
What is the purpose of the "DiscountService"? The "Discount" can already apply itself.
What is "price.add(BigDecimal.ZERO)"? That is "price" isn't it?
Don't iterate discounts, rather, create an "AggregateDiscount" that can apply discounts in sequence.
What is "BasicDiscount"? Why is it abstract, but has no abstract methods?
Apr 01, 2018 · Emmanouil Gkatziouras
This is bad code. Don't write code this way!
Anemic objects, data-function separation, externalized class invariant, switch-case statements, no cohesion whatsoever, strong coupling between classes... these are all bad things!
Mar 15, 2018 · Arran Glen
Thanks for your answer. I dislike the concept of a central runtime config server (as in it's a runtime dependency for my application). I do so simply because I've seen no convincing argument for it, and there are good arguments against.
I prefer how it's done in OpenShift / Kubernetes, as you mentioned, it is "mixed in" in the deployment. Also, this way it can be versioned in a controlled process, including review, and testing (it's a good idea to have tests for infrastructure itself).
I also dislike the idea of on-the-fly reloading. The only reason to do it that I see is when the infrastructure is not automated, and it just costs more to restart stuff. Or you can not do a rolling update for some reason.
So, while some of us might be stuck with non-automated infrastructure, which might need a central config server for example, I fear that projects might be using it for "newer" type architectures where it just introduces unnecessary complexity and dependencies.
Mar 15, 2018 · Arran Glen
This is just a bad idea. Why would you add such a central dependency to your distributed architecture?
Configuration should be "injected" into the app deploy-time at the latest. Changes to configuration should be properly versioned, not changed runtime.
It costs nothing (if automated correctly) to cycle a service if its configuration changes.
Mar 12, 2018 · Duncan Brown
The biggest problem with "Enterprise" Java today is not that objects have more than one responsibility, but that they have usually none.
Objects often share their responsibility with the rest of the application, that is why you have side-effects! Non-localized changes don't come from violating the SRP, they come from sharing your responsibility.
Every time you use DTOs, Beans, or even Getters you are no longer "in charge", therefore no longer "responsible".
So let's forget the SRP until we fix this first..
Mar 09, 2018 · Arran Glen
The whole Repo is a Microservices anti-pattern!
You are basically implementing a CRUD "Microservice" per Table/Entity. I couldn't find a single line of business logic in your repo!
I know finding a good example is hard, but this is exactly what people should not do with Microservices!
Feb 24, 2018 · Akash Bhingole
I don't agree with the assertion that we need a central entry point to distributed services because of security.
You are saying that it is "difficult to manage security" without central control. However Microservices are all about independent operation, both for the application and the team itself. Each service should stand on its own if possible and an API Gateway goes against this principle.
There are other ways to share authentication among services, through security tokens or federation, without a synchronous runtime (and organizational) dependency.Feb 18, 2018 · Alexandre Gama
Good explanation! However, I do think "lazy" initialization is overrated.
We probably agree, that constructors should not have logic in them, so constructing an object is not expensive. Certainly not more expensive than CDI constructing proxy objects anyway. If you are using service objects, those are probably already constructed and just need to be passed on.
So, although it sounds cool, and you demonstrate how to use it, I still wonder about the why. Would you implement laziness yourself if CDI wouldn't have it?
Feb 01, 2018 · Jordan Baker
I think we all know that REST today does not mean what Roy Fielding coined in his dissertation. And I don't just mean we're missing details. We are moving in a completely different direction, contradicting crucial parts of the original idea.
An article that mentions the original idea of REST should always mention that we don't mean that anymore when we say "REST" today. Whether the reason for that is pragmatism or just plain ignorance is irrelevant at this point. But to say the two have things in common other than the name is misleading.
Jan 11, 2018 · Jordan Baker
RMM is basically like the VMM (Vegetarian Maturity Model).
On VMM Level 0, you are not eating meat on 0 days per week; Level 1 is you are not eating meat on 1 day per week, and so on, until Level 7.
You can call youself a Vegetarian anytime you'd like of course, even if you are only Level 6 on the VMM, but it will likely confuse everyone else who knows what Vegetarianism is. :)
Jan 04, 2018 · Duncan Brown
Let's assume a "Rectangle" would contain "width and height" or "bottom-left-corner, top-right-corner". The usual stuff.
In both cases both "draw()" and "area()" would need this data, would they not? In which case they would be cohesive. In which case they belong together.
Jan 04, 2018 · Duncan Brown
Ok, so let's say it's not the same class that uses both methods. It's two separate classes inside the application.
I don't see how that is relevant if you are concerned with cohesion. Both "area()" and "draw()" work on the exact same data of the Rectangle. They are cohesive. You try to remove one, and you basically have to pull the insides of the Rectangle with it (the data).
Let's pick another example. If a class "Integer" had two methods "add()" and "subtract()". Would you separate the two methods if they were used by two different classes inside an application?
Jan 04, 2018 · Duncan Brown
Ok, I would just like to understand your point. Let's try one more time.
Suppose there is a Rectangle class (a business object), with "area()" and "draw()" methods as in the article. It is however only used (both functionalities) in a single application, not two separate ones.
Would this scenario still warrant a separation of the two functionalities into two objects in your interpretation of SRP?
Jan 04, 2018 · Duncan Brown
Rectangle is a "business object", and "draw()" is a UI functionality. You said you probably would not separate these two in a single application, because they are cohesive (work on the same thing). I agree.
Now you say that these "must always be separated". This seems to be contradicting your earlier statement.
You said that with UI and business, you don't want a change to the former impact the latter. What about a change to thet latter impacting the former? According to your own metric, separation will lead to more dependencies.
Jan 03, 2018 · Duncan Brown
Cool. I support your interpretation.
So if we imagine an enterprise application for example, where the classes won't be shared with another application, would you say that the "conventional widsom" of complete separation of UI functionality from Business objects is a faulty interpretation of SRP?
Jan 03, 2018 · Duncan Brown
The obvious problem with Uncle Bob's example is that it assumes two different applications will try to use the same object for two different purposes.
In enterprise applications I had the pleasure working on code reuse on this level of core classes is rarely if ever done. They might share libraries, but not "business classes". In microservices architectures code sharing is frowned upon even more.
Would you split the Rectangle if it was intern to a single graphical application?
Jan 03, 2018 · Carmine DiMascio
The point of microservices is to have independent, preferably development-team operated standalone applications. I see no need for using exactly the same configuration library at all.
As for dotenv itself, using Strings as values is too simplistic and error prone. Values should be typed (int, String, Duration, Date, etc.), should support units in configuration files (milliseconds, bytes, megabytes, etc.), and should support defaults and overrides.
My requirements: https://javadevguy.wordpress.com/2016/09/14/requirements-for-a-configuration-library/
Dec 27, 2017 · Yogeshwar Srikrishnan
You say: "You build software elements that are used in multiple systems..."
I think we are starting to learn that reusing stuff, as in sharing actual code between projects is not as great as we thought it was. Sure, there are things we reuse: database libraries, web frameworks, etc. But those are generic enough to be either off-the-shelf, or essentially independent.
Real "business logic" reuse is actually considered harmful these days. The whole Microservices movement, at least partially grew out of us wanting to have less (or no) synchronization points between teams. We are slowly learning how to do that, for example cutting problems along business boundaries instead of technical ones.
However, your point still stands I think. We do need "Enterprise Architects" (people who know the full landscape), "Software Architects" (people responsible for software quality and meeting non-functional requirements), etc. I don't think collective responsibility works, certainly not always.
Dec 19, 2017 · Arran Glen
In your discussion of sync/async communication, I think you are mixing several unrelated concepts, which should be perhaps discussed independently:
- fire-and-forget vs. request-response protocols
- blocking vs. non-blocking implementations/libraries
- one-to-one vs. one-to-many communication
These are all independent attributes and all 8 combinations exist, so maybe we shouldn't think of them as just sync or async.
Dec 18, 2017 · Duncan Brown
I have the feeling you miss some opportunities regarding abstractions.
You code in a command-and-control style, in that the CoffeeMachine commands all the components. It submits input and awaits output from all. It's not "wrong" of course, but...
Give the objects some more responsibility, some autonomy. When I ask the Grinder to grind some coffee, I would not submit the CoffeeBeans. I'd expect the Grinder to know where to get them. Similarly, let the BrewingUnit ask for GroundCoffee if it need some. Having a "controller" is not good OOP.
Power to Objects!
Dec 06, 2017 · Andre Luis de Oliveira Dias
I actually think that those use-cases should not apply to a correctly designed microservice. Here is why:
- "different data for different clients": Why would a microservice not offer a proper interface for all its clients? An API does not have to be a direct-to-database-CRUD API, it can (and should) be a business-relevant proper API for the functionality it offers. It also can have multiple interfaces for different use-cases or clients.
- "some services might not be able to offer a proper protocol": Those are "legacy" services, I agree that workarounds (like API Gateway) can be applied in this case.
- "api of service might be too find grained or too overly complex": Again, how was the API defined, if its unfit for its clients?
- "service partitioning": Partitioning is invisible to the client if done right. The client receives and follows links.
- "services scaling in/out": Again, clients do not see this. If clients see this, that means that some hardcoded URIs are used or something. We have lots of technologies already to deal with this transparently: DNS, Load Balancers, etc.
- "setup different API gateways": Sure, but why would you?
I'm not saying an API Gateway is useless, I'm saying it is a tool to patch a design flaw. Sometimes we do need that, for legacy software, or for things we can't influence/modify (organizational constraints). But a fresh design should not need it in my opinion.
Dec 01, 2017 · Andre Luis de Oliveira Dias
I think the API Gateway is needed for legacy services, but I would not expect a newly implemented microservice to need one.
HTTP was engineered to support transparent proxies, load-balancers and other intermediaries, and Hypermedia (REST) is exceptionally good at communicating routing information. Both of these technologies if used right, make an extra central, non-transparent server application unnecessary.
Smart endpoints, dumb pipes!
Nov 23, 2017 · Robert Brautigam
Well, the GitHub page links to the Cleancoders site, where you can purchase 15 lessons ($14 / lesson) to see how Robert Martin and Micah Martin design and write this application.
The first lesson even links back to this GitHub project to "follow along with the code".
So I guess it's real. Which really isn't a satisfying answer. I haven't contacted them and did not buy the online lessons either. Maybe there is somebody here who did buy those lessons, who could enlighten us about the reasoning behind this code?
Nov 23, 2017 · Ajaysankar J
I think the article is somewhat inaccurate at a few places:
- By "REST" you obviously mean RPC over HTTP, because you don't talk about Hypermedia, or Media-Types. But even then:
- HTTP has more than 4 verbs. OPTIONS and HEAD for example, or even PATCH.
- "REST" does not require HTTP. URIs support other protocols easily.
- "REST" is not point to point. Lot of indirections are built in: DNS, Proxies, Caches, Load-Balancers...
- "Smaller learning curve": The defacto redefinition of REST and the amount of misinformation on the net suggests otherwise
Nov 20, 2017 · Robert Brautigam
I'm glad I'm not the only one. Thanks.
Nov 07, 2017 · Mike Gates
With JPA you unfortunately can't. You would have to pick some other backend if you want to truly be asynchronous/non-blocking. For example Elasticsearch, Cassandra, etc (obviously depending on your use-case).
There are even asynchronous/non-blocking JDBC-based libraries out there if you want to stick to relational databases. Specific relational database vendors might have custom solutions too.
Nov 06, 2017 · Jordan Baker
You say that encapsulation means that fields are always private, and when outside objects need access, we provide that with getters and setters.
I think encapsulation is about the idea that an outside object should not need access to the data of my object.In fact, encapsulation and getters are two fundamentally incompatible things. Your article states, that objects should be "in charge" of their data. They can't be in charge of something they publish, can they? Things returned by getters are by definition then out of the control of the object.
Oct 27, 2017 · Mike Gates
I'm unsure whether this is a step in the right direction.
Asynchronous execution is sometimes useful, but what you really want most of the time is non-blocking execution. The two are not the same. The example code in the article is asynchronous, but it actually blocks at the database query (I/O).
This feature might give people a false sense that performance problems could be solved by just annotating everything with @Async.
Oct 13, 2017 · Chamath Kirinde
Kafka is a pretty cool piece of software, we use it to ingest metrics from thousands of IoT devices.
What I find a little difficult to get right is the reliability guarantees vs. throughput. It involves a very detailed knowledge of specifics like sender side settings, receiver side settings, topic settings, server settings, ordering and message keys, etc. It's not just "tuning", you can easily lose messages or re-order messages if you don't do it right.
I welcome the flexibility, don't get me wrong, but it is very easy to make a mistake too.
Oct 12, 2017 · Kapil Bhardwaj
Don't misuse enums for singletons please! I know it's in Effective Java, but it's still misuse!
And don't use singletons in the first place! This is the only message an article about singletons should contain.
The only cases where I felt singletons didn't bother me, is when used for stuff that has nothing to do with the "business" part of the application. Stuff like logging and metrics.
Sep 20, 2017 · Arnny Y
I know it's an old point, but you are probably comparing SOAP to RPC/HTTP.
REST is more than RPC/HTTP. It's about behavior, changing state through hypertext, creating a truly decoupled distributed system through a unified interface, etc.
Saying that it is for "accessing data" is grossly misleading. I know, the meaning might have changed, I just couldn't resist this time.
Aug 26, 2017 · Arran Glen
If I understand that right, by "encapsulation" you only mean hiding the address (server and the port) of the service?
I was using "encapsulation" in the software development sense, meaning that the data itself is hidden. Obviously if you have a central UI, that would have to know all the data formats of all services. That, by definition violates encapsulation of every service. Would it not?
I'm not making a judgement, just trying to clarify.
Aug 25, 2017 · Arran Glen
Wouldn't the encapsulation still be violated if you employ a gateway service?
I mean a central UI would still need to know all the details of all the services it speaks to even if indirectly. If not, than the "edge service" would need to. In either case "encapsulation" is not preserved.
Aug 09, 2017 · Arun Pandey
I know, your question is probably rhetorical and you really shouldn't use singletons in the first place, but the main reason not to make them enums is:
Singletons are not enumerations. Technically they might be similar enough, but they are semantically different, therefore it is a misuse to make them enumerations.
Aug 09, 2017 · Arun Pandey
The problem with singletons is not that you sometimes need to share an object, maybe even a single one between others. That can be done with simply passing the same object reference to everybody.
The problem with all the patterns you listed is that it couples the functionality with an unrelated design decision. To have a single something is an application level design decision, not an object level one (there are rare exceptions).
Second problem is that it couples other objects to the implementation of your singleton, not to an interface for example.
Jul 31, 2017 · Shamik Mitra
You are right, that most JEE architectures and projects violate "Tell, don't ask" and Encapsulation, consequently one could argue whether these can be called OO solutions at all.
The JEE people would argue, that the benefits outweight the cost, and OO is not that important if the whole thing works without it. Whether this is a good idea or not I leave to another discussion.
Bounded contexts and Microservices act on the architectural level, so OO is not a factor. The code for one bounded context, or one microservice can be OO however. And some OO concepts can actually be applied on the architecture level too, but I think that would lead this discussion too far.Jul 31, 2017 · Shamik Mitra
I guess my argument is that there are differences between "principles". There are "principles" that you can't pick and choose.
"Tell, don't ask" should not be up to debate. If someones says the code is OO, I expect that objects don't return data, instead I can tell objects what to do with the data.
In contrast, SRP is "just" an idea. It is an idea from someone who had decades of experience, that is true, but it is still just an idea. You can still do OOP if you are violating it.
So, if there is some case in which these two collide, there should be no question that SRP should be ignored in favor of "Tell, don't ask".
Jul 31, 2017 · Shamik Mitra
I think the 3 friends are comparing apples to oranges a bit.
The "Tell, don't ask" principle is a basic principle of Object-Orientation. "Basic" in this context means that the original concept of OOP is based on it and the concept of Objects are defined by it. Violating it shouldn't be an option at all. See Law of Demeter too.
The SRP is something Uncle Bob considers an important property of classes. That's it. It is definitely not a basic principle of OOP, regardless whether one agrees with it or not.
Jul 26, 2017 · Lea Maya Karam
SRP does definitely not lead to higher cohesion and lower coupling (in the original object-oriented sense of the words), quite often the opposite.
Let's take the example you've linked to. It splits up "area()" and "draw()" methods of a Rectangle into two separate classes. These two methods obviously work on the same data, so inside a single class they were cohesive, but after the split they are now coupled. Worse, you will have to actually grant one of those classes access to data of the other thereby probably also violating encapsulation.
Jul 24, 2017 · Dulaj Atapattu
The easiest way to think about this is: Are singletons enumerations?
They are clearly not in my opinion, as they are not a limited set of constant values. They are not even values! Values need hashCode() and equals(), most singletons (which are mostly "services") do not. Enumerated values have an ordinal index, singletons don't need that.
Implementing singletons as enums is a clever technical trick, but its misusing the meaning of an enum, isn't it?
Jul 21, 2017 · Dulaj Atapattu
I think this is a clear misuse of enums. Even if it is technically easier to make singletons this way, it is still wrong.
On related note: singletons are an anti-pattern anyway, you don't (shouldn't) need them.
Dupe of this, this and this.
Jul 18, 2017 · Grzegorz Ziemoński
I agree that FP an OO can mix very well. Concepts from one can be used in the other (not all of course).
But, I don't really agree with your interpretation of the "Single Responsibility Principle". I agree with the article here, that the SRP creates more confusion than it is worth.
I propose we just ignore SOLID for a while. Not because it is wrong, but because it is just so confusing and inprecise.
Let's get back to the original concepts that are actually far more important: Encapsulation, Information Hiding, Coupling, Cohesion.
Jul 18, 2017 · Grzegorz Ziemoński
You mean you doubt Object-Oriented programming "as understood by most of its practicioners"? In that case I doubt it too. Just because someone uses an OO language does not mean that he/she is an OO developer.
I suspect the same would happen to functional programming if it ever really takes off. Sooner or later someone will say: Why can't I have side-effects in my functions, it's just easier that way? There will be mutable state. There will be unpure functions. Anyone who points out that it was not supposed to be that way will be possibly labelled "purist". :)
Jun 14, 2017 · Héctor Valls
Yes, partly, but I also mean Media-Types.
Media-Types are basically the Classes of HTTP, the type information, the definition of behavior to the data that is supplied.
The "application/json" media-type, which is generally used by "REST" APIs corresponds almost exactly to DTOs and other pure data structures.
I'm not saying that you should always go RESTful, I find it only ironic that AJ mentioned it as an example of pure datastructures being good, when it was actually invented to be the opposite, and then similarly "re-defined" by the community, like object-orientation.
Jun 13, 2017 · Héctor Valls
Incidentally, the original description of REST (by Roy Fielding) did actually say that you should provide the meaning with the data you are supplying. You are not supposed to deliver a pure data structure, but also specify what the client can do with said data, even by optionally sending code with the data.
Jun 13, 2017 · Héctor Valls
I think I agree with what you are saying, but not with your implicit conclusion.
I think all that is just cultural. We are "brought up" procedurally. Most of us learn algorithms, procedures first. People are thought Logo and Basic in school. Most katas and coding exercises/challanges today are algorithmic.
In my first projects I was told I need to always separate UI from Persistence and from Business Logic. Layered architecture is good. I never questioned that, I even thought it was "clean" in some sense.
What I am trying to say is, that the fact that something is popular or mainstream does not mean that it is the best, or even a good option.
Jun 13, 2017 · Héctor Valls
I also don't like OO "purists". I simply try to write maintainable code, and in my experience the Article is right, DTOs, Controllers, Managers, anemic objects, etc. make the code less maintainable.
It's as simple as that.
Maybe you're right, we should name the concept of grouping "data and behavior" always together something different than "object-orientation", because it already (unfortunately) accumulated a lot of baggage.
Jun 13, 2017 · Héctor Valls
I also read Yegor's blog, he has some really cool posts. But.
I'm not onboard with objects exposing data. Neither the File nor the User implementations should expose data (id, username, content) like that, even if those methods technically do not qualify as accessors (well "id()" does actually).
Objects should expose "business-relevant" behavior. Arguably "id()" and "username()" are not business behavior. They are not "dumb structures" anymore, that's true, but they are still not "behavior" enough in my opinion.
Jun 06, 2017 · Artem Rukavytsia
The hashes can collide, but HashMap resolves hash collisions by simply using "equals()" in addition to hashes.
Hashes are only used to roughly distribute objects into "buckets". You can return a constant number as hashcode and HashMap would still work (although it would not be very performant).
In other words, hashes don't need to be unique, nor strong in the cryptographic sense, and there is no problem using Strings as keys in HashMaps.
But you are right, there might be some unforeseen performance implications if poorly choosen.May 19, 2017 · Mahan Hashemizadeh
Ok, so let's assume the top level is called 'petstore'. That is a good name, I can derive some information about the app from this name.
Let's go deeper. Under 'petstore', there are 4 modules: "adapter", "configuration", "core", "periphery". I have no clue how these things relate to a petstore. I would have expected 'pet', 'owner', 'appointment', 'payment', things like that.
So, let's continue one level down into 'core', that should contain our logic, right? The packages there are: 'boundary', 'domain', 'usecase'. At this point we are 2 levels down, and we still did not see anything related to what the application is supposed to do.
Let's continue into 'domain'. This contains: 'Customer', 'FirstName', 'MiddleName', 'Suffix', 'LastName', 'Name'. Some business relevant things at last, however in a detail which would make it difficult to get an overview in any normal sized project. If there are 20 core "things", would all of them be just dumped into this package? Also, all are anemic objects (java beans), so there is no indication of how these are used and for what.
All in all, if your initial problem was to have an architecture that makes the intent of the application clear, helps to understand the concepts and how they relate to eachother, I think you would have to "invert" your tree somehow. I consider the stuff at the bottom important and relevant, and the things in the first 2 levels are the details that would not interest me at all if I'd try to understand the application.
May 19, 2017 · Mahan Hashemizadeh
I agree with your first section, that from the package hierarchy in Fig#1 it is unclear what this application is supposed to do (intent).
However, I would argue, that your refactored packages in Fig#2 have the same problem. It is still not clear what this is application is for. Is it a petstore? Is it a patient-database? Is it a real-time video conferencing tool? I have no idea, could be anything.
Arguably the package hierarchy should reflect the intent (contents), not the technology or architecture paradigm...
May 17, 2017 · Robert Brautigam
Just to be clear, I'm completely with you on what should happen. I agree that P should do the work for M.
However, the Law is formulated in a way that introducing additional methods can actually "circumvent" the Law. It's not because of String, but because you can introduce any number of additional methods to "unpack" a call hierarchy.
Even if "getName()" returns a complex object, you can introduce a new method for that complex object, for example "send(Name n)". So "send(Person p)" calls "send(Name n)", and that calls then "send(String s)" all in the same NetworkConnection. In each of those methods I am allowed to call the next level method on the parameter, so technically this solution is "allowed".
May 17, 2017 · Robert Brautigam
Good point, you are not wrong. Technically this would not violate the Law, the same way that wrapper methods technically solve the problem of knowing other objects. However, arguably the spirit of the Law is still violated (we did not achieve a lesser coupling for example).
The original article actually mentions the transformation you demonstrated, as a way to transform any code that does not conform to the Law to one that conforms to it, at least technically. It might be a valid step that leads to an easier refactoring perhaps...May 17, 2017 · Robert Brautigam
Thanks. Is Java a good OO language? I think it's good enough. Or to put it differently: all the terrible/bad Java projects/libraries we see, I don't think it's because of the language.
As an anecdote: I had the pleasure of working with Scala for 2 years on a project (this was before Java 8). After the first year I thought to myself, I will never ever do another Java project, ever. It was a joy and very refreshing to work with Scala.... But, after the "honeymoon period" started to wear off, I started to notice the same things as with Java. Bad libraries, bad code (some mine admittedly), multiple different paradigms (functional, object-oriented, procedural), paritally mixed in the worst possible way, etc.
These days, I find it easier to talk about object-orientation in Java, because there are just not that many language features to distract people. Just my opinion...
Apr 20, 2017 · Grzegorz Ziemoński
Short answer: Yes.
Long answer: I'm not sure Mr. Fowler was aiming for OOP in that article. Data structures are completely ok for the functional or the procedural paradigm for example.
Also, I don't like the word "incorrect". It's not incorrect, it's just not following OO principles.
Apr 20, 2017 · Grzegorz Ziemoński
Just because an object does not have an identity doesn't mean it shouldn't have behavior!
The distinction between "Value objects" and "Entities" is largely irrelevant from the point of OOP. Every object is supposed to have "real" behavior, that is the actual essence of OOP.
So if you are doing OO, then even Value objects shouldn't have getters nor setters. They should have some business-related responsibility. Calculating something, printing something, etc.
Apr 20, 2017 · Grzegorz Ziemoński
Watch out, control through setter/getters is just an illusion!
Ever wanted to allow 'null' on an variable with accessors, and thought: I wonder who uses this variable?
Ever wanted to remove a variable and had to search all references to make sure nobody uses it?
Ever wanted to change the type of a variable, or split it up to multiple ones?
If you publish your internal variables with accessors, you by definition lost control!
Apr 14, 2017 · Duncan Brown
An OO solution would be to represent the things you want as normal Objects:
public final class FilteredJvmProperties {
public FilteredJvmProperties(String filterExpression) { ... }
public Response toResponse() { // Or whatever you need for your application
...
}
}
Mar 17, 2017 · Grzegorz Ziemoński
Ok, I think I see the problem :)
So let's assume for the moment, that your business requirement says you need to "read data/fields" from the database. In this case, you might go for defining "getName()", "getText()" or whatever in your Activity or BlogPost object. That is your Domain. It does what your requirements say.
But, as you said, that is not really what we want. We actually want to "display all Activities" (or BlogPosts) to the user, and that is a completely different requirement. It would imply that we have a "display()" method instead of getters in our domain object for example.
This is at the core of everything you wrote about in the past too. Separation of Concerns, Single Responsibility, Screaming Architecture, etc. These all pertain to the actual business functions that you want to model. If you want to "display" a "BlogPost", then OOD tells you that this function belongs with its data, into the "BlogPost".
I know that displaying stuff is often not considered first class citizen among requirements, but it is a requirement, therefore it needs to be modelled just as deleting a BlogPost needs to be modelled.
And again, I'm not arguing that everything needs to be OO. I actually think the functional approach is at least just as valid. I only want to highlight, that this particular architecture approach that you wrote about goes against some basic OO principles. That is all.
Mar 17, 2017 · Grzegorz Ziemoński
1. Anemic Objects aka. Records can never be part of the business model, since that indicates that there is no associated function or responsibility. This simply doesn't make any sense in OO. You might argue that your solution is not based on OO, in which case it's OK, although it is somewhat confusing, because you usually argue for OO.
2. Reading data from the database is not a business function, unless you are specifically writing a database library. Since you include "Activity" I assumed your domain is something different. In a normal application reading from the database is a technical detail, not part of the domain.
3. Package hierarchy I had in mind does not reflect business "importance", it should reflect abstraction levels. Higher levels are more abstract, lower levels are more concrete. Hence, higher levels can not (should not) reference lower levels. Lower levels can however reference higher levels.
4. Non-final IDs. What happened to screaming architecture, clean code and readability? If the Id should not be modified during the lifecycle of the object, what else would express this the best than making it final?? You just make it final, and make it a constructor parameter. I don't see how that's harder to do than defining a setter.
5. Again, reading from database is not a business function. "Displaying all Activities to the User" might be a business function. "Deleting all Activities" might also be a business function. How would reading from the database benefit any users?
Mar 17, 2017 · Grzegorz Ziemoński
This can't be right. Uncle Bob actively promoting an architectural style building on Anemic Objects?? It's not even an exception based on some tradeoff, it is integral to the concept?
The linked cleancoders repository is full of anti-patterns. Anemic Objects, higher packages referring to packages below, non-final IDs, more technical terms than business terms, and of course separation of data from function.
What is happening?
Mar 14, 2017 · Grzegorz Ziemoński
Exactly my point. We agree that OOD is artificial, that does not mean that it is "wrong", or that something else that does not seem quite as much artificial is better. It is even sometimes counter-intuitivein in my opinion. Still, this is not an argument against it.
It might be a cultural thing. We all (all the people I work with) started out as procedural programmers. You know, Basic, C, Pascal, etc. The programming games (like CodingGame) we play are Procedural. Even programming courses are all procedural, they teach you algorithms first, how to instruct the machine to do something (conditions, loops, etc.), even in Java courses. We've been, quite simply, "damaged".
Now, the claim that "interactions" (e.g. Procedures) shouldn't belong to the participants is quite telling. It is the antithesis to OO, which claims exactly the opposite, that interactions should be decomposed according to the responsibilities of its participants.
This is artifical, but in no way arbitrary. The simple truth is, that everything will change eventually, there is no "safe" code that we can extract. Our only chance is to localize changes, so that when we have to change something we don't have to read the whole application to understand the impact of some code. OO is a very effective way to localize changes.
In both examples given in your and the original Article, any change to any of the Data "Objects" or Interactions would basically ripple through every other "Object". New type of account? Amount represented differntly? Introduce currencies? Introduce limits? Now you have 100 use-cases (non-localized!) that need to be changed. This will not be maintainable.
Mar 14, 2017 · Grzegorz Ziemoński
I think this approach is simply misguided. The 2009 Article describing it comes off as a rant against Object-Orientation, at one point even praising FORTRAN as being more easily readable and verifiable.
The authors simply argue for Procedural Programming, the main argument being that that decomposing a problem is simply too hard and in some cases can not be done properly or easily.
Well, there is unfortunately no requirement that our profession must be easy :)
Mar 12, 2017 · Shamik Mitra
So there is a com.employeemanagement.entity.EmployeeJpaEntity somewhere, that looks exactly like EmployeeDomainObject, except it has JPA annotations in it.
That is not independence, since they are the same thing conceptually, and will always change together. You literally just copied your business class into the technical layer.
Also, the Command looks suspicious. It is clearly a technical thing (since it knows about the JPA bean and has no business methods), but seem to be required to be passed along the business object (see Main).
All in all, I think this needs some more work to be a fair demonstration of the subject.
Mar 11, 2017 · Shamik Mitra
Could you please clarify what your "domain" for this problem is? Are you implementing a persistence framework, or an actual application doing something?
If you are implementing a persistence framework only (that is, Persistence is your domain), then wouldn't you require that users of this framework annotate their "entities" with JPA? Because that would defeat the purpose of building a technology-independent persistence framework in my opinion.
Mar 01, 2017 · Grzegorz Ziemoński
I agree that Optional it better than just allowing null, since it's more explicit. However, why not go just one step further and make it more expressive? Which do you prefer:
withColor(Optional<Color> color);
or
withColor(Color color)
withDefaultColor()
I agrue, that the first is not clear enough. If the color is missing, does that mean no color (as in transparent), or will there be a default color? The second solution with 2 methods is clearly more expressive, wouldn't you agree?
Mar 01, 2017 · Grzegorz Ziemoński
I agree, but I argue for no null or Optional as argument. None.
For the simple reason that it's just not expressive enough. To give null as argument to some method is just plain programming error, and should never be actually allowed. Optional is a bit better, but not by much.
Mar 01, 2017 · Grzegorz Ziemoński
That is the kind of code where Optional makes the problem worse in my opinion.
If the 3 parameters are dependent on eachother (which I assume since they go together into a method), that suggests different use-cases which I argue would not be a single method. It could be different methods, or even different classes or something like that.
If the 3 parameters are independent, then that wouldn't require a single method with 3 parameters.
I think the problem with many optional arguments might be a non-existent one, assuming a sound design. Remember, the design should reflect the meaning. I have no clue what the meaning is if 1, 2 or all 3 of the parameters are missing in your code.
Mar 01, 2017 · Grzegorz Ziemoński
The main reason not to use Optional as an argument, is that we already have a much more expressive way of describing an argument that is not there. It is done by, well, not defining the argument in the signature. I.e. overloading.
I agree with all the technicalities, like using Optional only as a Monad (no get()), and the convenience sometimes it gives us. However, you shouldn't force your API users to understand something which is only there for your convenience.
Feb 26, 2017 · Arun Pandey
I know we "used to" provide base classes for interfaces and it was considered good practice. I think it is actually an anti-pattern.
Think about what it means. If you require others to use the base class, or a certain way of implementing the interface, then there is no point defining the interface, is there? If you just want to "help" the implementer implement "common" stuff, then the expressiveness of your interface is poor.
What I would prefer are full implementations, that may define alternative interfaces.
Feb 13, 2017 · Grzegorz Ziemoński
Very balanced and objective article :), I could not be so "forgiving" and would say that layered architecture is (almost) an anti-pattern.
However, I think you use "separation of concerns" differently than I would. Technical "concerns" like Presentation, Persistence are not real concerns. Normally when we use the term "separation of concerns" we (should) use it for business-related concerns. E.g. functionality. Same for "single responsibility", which you wrote about already.
Dec 19, 2016 · Riaan Nel
A solid technical description of the pattern, but this is perhaps not the best solution to the problem given. I argue for more OO.
There probably shouldn't be any "InterestCalculation" or "InterestCalculationStrategy", there is probably no such thing in the Domain anyway. There is only "Interest". There should be a "SavingsInterest", or even "NoInterest".
Also, it shouldn't be the Interest's responsibility to decide what kind of accounts it applies to, instead the Account should have an Interest.
Dec 15, 2016 · Robert Brautigam
Let me nitpick a little :) REST does not assume CRUD, RESTful HTTP does. REST is just a set of architecture constraints, HTTP happens to fulfill (enables fulfilling) those constraints.
Anyway, you are right, we are "forced" to model our problem in some paradigm to use RESTful HTTP. But, don't we do that all the time anyway? You are also sometimes forced to solve your problem using Object-Orientation, Procedural Programming, Functional Programming, or in a Relational Model, etc.
We choose a paradigm because it has some benefits that we want. To get those benefits however, we have to use the paradigm well. We are not really "forced", it's just that we want something in return.
I happen to believe HTTP is a great, very powerful, very underrated integration technology. Both it, and the REST concepts are actually very well defined in my opinion. The only problem with REST is, that it is not easy to understand. You have to spend a lot of time reading, discussing it, and you will still get it wrong the first couple of times. Hence this article!
Dec 15, 2016 · Robert Brautigam
You might be right that is was not intended for "contract evolution". Fielding himself said he expects (some modest amount of) standard Media Types. I don't think this is realistic though if you include (Enterprise) "internal", programmatically parseable APIs. This does not change the idea however.
If I split the FullName of a Person to FirstName/LastName. Isn't that now an alternate representation of the User? Doesn't that warrant a new Media Type?
If my service serves "application/hal+xml" how does the client know that that is a Person? Or an Order? Remember, messages must be self-descriptive, and I don't want to "just" present to the end-user, I would like to process it automatically.
Also, if you want to list your relations (links) specific to your use case as you say, where do you do that? I don't see any place other than the Media Type to specify things related to the message. I think you can't just add additional relations to hal+xml format, at least not without calling it something else, thereby effectively creating a new Media Type.
Dec 15, 2016 · Robert Brautigam
By introducing a new relation you are implying that the URI has changed.
If it is still the same user there is no reason to have two different URIs identifying the user. Remember the URI means Uniform Resource Identifier. You probably don't want two different Identifiers for the same thing.
The technicalities, like syntax of the message (representation), meaning of fields, what links there are, and even the SLA (performance guarantees) are part of the Media Type. Therefore either the Media Type describes how the versioning is done, or the Media Type itself must be versioned.
I agree with your other points though.
Dec 13, 2016 · Sarah Davis
Yes, sometimes a bad product can dominate the market.
However, I do not agree with delegating "intrinsic" quality to the PO. There is a facet of technical quality which can not be directly measured, but has a huge impact on the continuous performance of the team. It is the internal "design" of the system. Architecture, technology selection, down to individual classes and methods.
This is in my opinion what differentiates "high-performing" teams from average ones (PO and external factors being equal), and it should be owned by a hands-on Architect.
Dec 13, 2016 · Robert Brautigam
I didn't argue either way, and I agree, do what is best for you / your project.
However, to decide what is best for you, first you have to know what it is you are embracing or rejecting, and you should also know something about the consequences of this decision.
Now, you may know all this, but I have seen plenty of projects where the team didn't even know that they were not doing REST as originally intended (there was no decision to reject anything, it was just the way things were). This is because the resources on the net are more confusing than helpful in this regard. Hence this article.
So it's not about right or wrong, it's about calling attention to something you might not know, to enable choosing what is best for you.
Dec 13, 2016 · Riaan Nel
1. It makes me sleep better at night :)
2. You can thread generic parameters through without casting. (This I could perhaps explain in an article sometime).
Admittedly, none of those is necessary all the time :), so I don't have any problems with mutable builders, as long as the constructed object is completely decoupled from the builder after the instantiation. That is, the builder can't change the object that it built previously.
Dec 13, 2016 · Riaan Nel
The fluent thing is cool (for me) because it could be implemented with completely immutable objects, including the builder! The builder in this case would not return itself, but a new builder with the new parameter set.
You're right though, it can be overdone.
Dec 12, 2016 · Robert Brautigam
The "id" is a tricky beast. The question is always why is it there?
Often the answer is "just because". We had the Id from the database, so it "costs" nothing to publish it in the json response.
I think it actually costs a lot because it increases coupling, especially if it is a technical id. Some business entities have natural Ids, like Transaction Id, ISBN for books, People may have an employee number, etc. Those are good candidates to publish if it makes some sense.
Technical Ids (the sort that is generated by the database for example) are rarely if ever good candidates to publish. These are technical details and serve no business purpose at all, so they should be hidden.
Dec 12, 2016 · Robert Brautigam
Putting the version number in the Media Type does not break the client when new incompatible versions are introduced, because of Content Negotiation.
The client always supplies a list of Media Types it understands, for example person-v1+json. If the server introduces a new person-v2+json Type, the client will not even see it as long as it does not ask for the new version explicitly, therefore it's completely backwards compatible from the viewpoint of the client.
I rarely see Content Negotiation demonstrated or explained in tutorials, this might be one of the reasons it is rarely used by API designers.
Dec 02, 2016 · Daniel Stori
So, you want a Coke. Let's say you already have a Fanta, and they are basically the same, except for the color.
Inheritence: You take the Fanta, and apply some black food coloring. BAM! You have Coke.
Composition: You don't change the Fanta. Instead you get a black mug, and pour your Fanta into that. Problem solved.
Enjoy:)
Nov 03, 2016 · Per-Åke Minborg
Cool stuff, thanks.
For the actual example, I would perhaps go for a different design though. For the tuples, instead of
TupleBuilder.builder().add("Meaning of Life").add(42).build(), I would just go fortuple("Meaning of Life", 42). I don't really see the benefit of a builder here, as there are no real choices to make.The same for maps. I would perhaps simply go for
toMap(Tuple2<A, B>... tuples), instead of the alternating type builder you introduced. It is still safe, it is still easy (or maybe even easier) to use.Oct 29, 2016 · Armel Nene
Yes, Companies/MBAs are to blame for a lot of ill.
But, we have to look at eachother too. A lot of projects I see do not have a "real" architect. Somebody who is in charge, or is responsible on the long run. There is usually no explicit direction, or hands-on steering by the architect/lead.
Technical quality is ultimately with the Team (the Architect in particular), and not the "Company". So, if the software is bad, I would first blame the architect, and to a lesser extent the team.
Oct 27, 2016 · Dave Fecak
I've been involved almost exclusively in JEE projects since 99, including being lead architect of a multi-million euro international project.
I think there is a misunderstanding that big projects would necessarily need "big" libraries. In my experience exactly the opposite is true.
The bigger your project, the more you have to think about long term viability and maintainability. In a "smaller" project you can settle for "just works", e.g. JEE if you want, no big loss. But at some point good architecture, design, OO principles become far more important.
At this point, you don't want something that essentially dictates how your software (and architecture) should look like. You want the requirements to dictate the design, not the tools you use. Smaller libraries in general let you do that, JEE does not, Spring (arguably to a lesser extend) does not either.Oct 27, 2016 · Armel Nene
Sorry for the bleak picture, I didn't have my coffee yet today... Just to spin this in a positive direction, these are (some) things I think we should concentrate on:
How to measure/evaluate competence in our profession.
How to reform HR and hiring practices.
How to not burn out.
Career path for techies that doesn't involve management.
Thinking about individuals instead of "resources" in projects.
Balancing political correctness, professionalism and honest feedback better.
Evaluating companies of their competence, or willingness to change.
Oct 27, 2016 · Armel Nene
The truth is most companies (exceptions excused) are incapable of evaluating "competence", sometimes by design. I don't think you have to be "competent" to succeed today, and I don't even think it increases your chances that much. You just have to play the "hiring" / HR game reasonably well.
Even on these very pages experienced people sometimes disagree what "competence" looks like. And to be honest, most of the recent projects I've seen don't really care about competence or quality that much anyway. 10 resources are 10 resources, right?
Oct 27, 2016 · Dave Fecak
There are literally thousands of libraries/frameworks out there, I probably couldn't list them if I tried.
Dependency injection libraries (like PicoContainer, Guice, Dagger, JayWire, ...), web libraries (Wicket, Tapestry, Play, Spark, ...), Cache libraries, NoSql Databases, Stream processing libraries, Batch processing libraries, Cluster computing libraries, Queueing systems, JavaScript Frontend libraries...
All of those, in combination with eachother or even alone, are alternatives.
Oct 26, 2016 · Dave Fecak
I would just like to mention that there are other options than these two. Every time there is an article JEE vs. Spring, people may get the idea that they have to choose one or the other.
Infact you can, as the author actually says, just evaluate your requirements and build an architecture around that. No need to start with more than you need!
Most of the (enterprise) projects in my recent memory could have been done without JEE or Spring, and in my opinion would have been better off most of the time.
Sep 24, 2016 · Enrique Molinari
Good questions. I have no doubt that many people in the JEE community are well versed in OOP. The current state of JEE might be a consequence of historical (you mention the JEE heritage), or even political reasons, I don't know.
However, nobody from JEE seems to say: What we're doing might not be OO, but we will stick to it because [reasons].
This I find problematic, because it is confusing, especially for newcomers. Java is supposed to be Object-Oriented, and many look to JEE as how things should be done. I'm glad that some people caught on to this as you say, but it seems JEE is not even aware of any problems in this area.
Sep 24, 2016 · Enrique Molinari
Interesting topic! Experience seems to contradict your conclusion though. Most of the projects I've seen (most were Java/JEE), there were very little OO done, even by experienced devs, and if there were "real" objects, they were mostly by accident.
Just look at JEE. Clearly, most of the people involved are very experienced and knowledgeable. Still, it's mostly procedural, I assume on purpose. Are these people right when they decided not to use/encourage using objects? I think David West and Uncle Bob would say no, David West for sure :)
Sep 13, 2016 · Sam Atkinson
I think the example you bring slightly mixes usage with function.
If I write a "ConnectionPool" class, I just give it a constructor with the required dependencies. One might assume, that you only need one of these in a single application, but I still wouldn't code that into the class itself. The class itself functions perfectly well without the assumption, so I would just be putting additional constraints on the usage without any benefit.
I think the instantiation, unless the class is application-specific (i.e. not re-usable), is not an implementation detail.
Aug 27, 2016 · Duncan Brown
I agree almost completely with your assessment.
I do however think there is a significant difference between "application/json" and "application/vnd.whatever". In the second case, I can build a proper RESTful HTTP client that will use content negotiation, and will know how to interpret the data. I need that information for technical reasons in other words.
Your point that these are not well documented, therefore useless is not a technical issue. It's an important point, but it's not a technical one.
Jun 13, 2016 · Sam Atkinson
I honestly did not want to start a flame war, just wanted to offer an alternative view for the article, that this "crisis" might not be a bad thing.
You are right, this is of course my personal opinion, nothing more. And I'm not just critisizing, I'm working on alternatives, like JayWire.
However, please do not confuse popularity with proper engineering, I hope you agree that popularity is not an argument at all for technical excellence.
Anyway, this topic is too big to fit into a reply, maybe we see eachother at some conference where we can continue the discussion. :)
Jun 13, 2016 · Sam Atkinson
As Mr. Plinkett would say, it's difficult to articulate what is wrong with JEE, because it basically involves everything.
Let's start with JSF. I don't think anybody at this point seriously argues for it, I'm not sure whether you would. I think Apache Wicket for example is way clearer, doesn't need multiple DSLs to work, doesn't need extra libraries just to be usable.
The whole concept of an "application server" is outdated. Great article by Eberhard Wolff.
CDI is conceptually broken. No separation of concerns, classpath scanning, annotation hell. My personal alternative (I'm the author): JayWire DI. The first DI framework (it's a couple of classes actually) without magic.
And so on...
Jun 10, 2016 · Sam Atkinson
I've been a Java EE Developer/Architect for 17 years now, and I have non-trivial amount of "discomfort" regarding the official specifications and implementation stacks.
For the argument that we're invested: Yes, maybe. But you have to know when to fold. I would see this as an opportunuity for something new, and just maybe, something better.
Whether we want to solve the same problems again: Yes, maybe. Some problems JEE solves are not even problems, and others (most) have better solutions already. (I'm talking about basic stuff like DI, Persistence, Web).
Jun 08, 2016 · Sam Atkinson
You are setting up a false dichotomy. I don't only have the two choices of using annotations or config files.
The third, arguably obvious solution is to use real (pure) OO Java Code.
Curious, that you mention DI and Annotations as a positive example for separating configuration and code. I would argue that CDI is a prime example of mixing configuration and code. What does "@ApplicationScoped" have to do with my class? Absolutely nothing! It is not only mixed together, the two have nothing to do with eachother.
Jun 06, 2016 · Sam Atkinson
I completely agree with the message that more code (characters) does not equal worse code.
The notion to the contrary (curiously enough) seems to come from the Enterprise Java devs, who insist that "boilerplate" code should be avoided at all costs. And "all costs" seem to include throwing OO overboard, and introduce Annotations to replace real Code.
May 04, 2016 · Mahlatse Makalancheche
There is a distinction between the technology and intent. Yes, annotations are supposed to be meta-data, unfortunately that is not how they are used in this case.
For example, the METAINF file contains meta-data. It is information about the jar file. Such as who created it, checksum maybe, etc. The @Override annotation is also meta-data, it is additional information about intent that is already there, but can not be expressed otherwise.
The @Entity annotation is not meta-data. It is in fact an integral part of the class functionality. I'm not giving additional information about content with it, I am defining how the class (or others) should behave.
It does not help if it is in XML. If it defines functionality instead of just describing it, it is not meta-data, it does not matter where it is or in what language.
May 04, 2016 · Mahlatse Makalancheche
JPA Entities are of course bound to persistence. That is why they are called _JPA Entities_. Also, they are not just POJOs. They are in fact annotated with @Entity at least, more often than not with much more, including JPQL statements.
Don't confuse intent with code. Just because the intent is not written in code does not mean that it does not exist, arguably the dependency is worse, since now it's hidden. In other words: You could not use your "POJO" without JPA, that means it is bound to JPA. If you could, why do you need the annotations?
Mar 14, 2016 · Ivo Woltring
Sometimes you really have no other option, and I guess CDI even encourages such designs unfortunately.
Still, I would rather try to refactor to avoid direct field injection, either through constructor arguments, or if it is an "optional" dependency, a setter.
Mar 10, 2016 · Joy George
There is one minor but crucial difference in meaning when talking about Employees in the context of the application and in real life. The Employee class in the application does not (usually) represent the real-life Person. We are not (usually) modelling the actual human employee, however close we get, instead we are modelling the business or application "view" what an employee is.
Whether the Employee class should have calculateSalary() is still debatable, but your argument "should an Employee be able to decide their own salary?" does not apply in my opinion.
Mar 10, 2016 · Joy George
There are 2 separate issues here. Whether the class should contain the actual code to do something, for example calculateSalary(), or save(), I'm with you on this one, they probably should not.
Whether the class should offer those methods (be responsible for the functionality, bot not necessarily the implementation), that actually depends, but in general yes, they should be. Models should define a save() method, Employee should define a calculateSalary() method, etc., unless there are good (semantic, not technical) reasons not to do it.
Mar 09, 2016 · Joy George
I strongly disagree with your premise. You say:
That is the definition what an object should be in an object-oriented setting. The encapsulation of data and the allowed operations on them. I don't see how you can argue against that, unless you are arguing against object-oriented programming and for procedural programming. Is that your point?