<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Renato Santos</title>
    <description>The latest articles on DEV Community by Renato Santos (@renatolsjf).</description>
    <link>https://dev.to/renatolsjf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F813451%2F33353be0-a498-4046-80e6-6bd7ce892f97.jpeg</url>
      <title>DEV Community: Renato Santos</title>
      <link>https://dev.to/renatolsjf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/renatolsjf"/>
    <language>en</language>
    <item>
      <title>AI Engineering by O'Reilly. Book review.</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Mon, 24 Feb 2025 15:34:28 +0000</pubDate>
      <link>https://dev.to/renatolsjf/ai-engineering-by-oreilly-book-review-42pa</link>
      <guid>https://dev.to/renatolsjf/ai-engineering-by-oreilly-book-review-42pa</guid>
      <description>&lt;p&gt;I have to start off saying I am not sure how much of this essay—yeah, a long text is incoming—is an actual review of the book and how much of it is me giving my opinion about AI. I have made it a goal to read and review several AI books this year. Mind you, I'm not an AI engineer or specialist, and this is but the first book on my list. One could argue that I am in no place to opine about the book or AI itself. I guess that's a fair statement, but I have had my fair share of interactions with AI, and I'd like to think I have a couple of well-founded ideas.&lt;/p&gt;

&lt;p&gt;The first thing to have in mind is that AI engineering is not a machine learning book, so don't expect to dive into it and get a fundamental understanding of ML if you don't have it already. Surely, you'll get a few concepts here and there, and you will get a better grasp of how things work, but a lot of things you read will leave you wondering about the real meaning and how it's used. This is not a bad thing; with AI and foundational models becoming a part of more and more products, every software engineer may need to understand AI in general, but with no deep knowledge of machine learning. ML engineering and AI engineering are, at this point, related but different things.&lt;/p&gt;

&lt;p&gt;The book does a fantastic job of presenting concepts on how AI and foundational models work. You'll understand the rise of foundational models, sampling, model evaluation, model security, mitigating hallucinations, prompt engineering, and dataset engineering. Sampling and prompt engineering are especially helpful, as I believe those will build the base that most will use to adapt foundational models to their applications—yes, I agree "prompt engineer" is not a job position, but it is a valuable skill to have; it's just not enough to build production-ready apps. Techniques such as RAG and fine-tuning are also well explained, but there is not enough information for you to apply them.&lt;/p&gt;

&lt;p&gt;When talking about machine learning concepts, though, the book can get a bit tedious. I have mixed feelings about it. Some parts have some fascinating theoretical concepts, while others have no real depth of knowledge, and neither does the book follow a path to build real understanding. As a non-ML engineer, I found it challenging to read ML-related content.&lt;/p&gt;

&lt;p&gt;The author is clear on how quality assurance is a must for AI applications, or the risk is far greater than the benefits. She then proceeds to show a handful of techniques on how to evaluate models. One of them is AI as a judge, in which a model judges the output of another. The judge can be either an LLM or a smaller, more specialized model. This is certainly a fair point; judging is easier than generating. I mean, how many times have you seen people judging the work of others without being able to do the job themselves? Actually, the author mentions we should get creative to solve some of the AI shortcomings; I can't shake the feeling that every time she mentions that, the suggestion of using AI to solve AI issues is coming right next. I'm obviously not implying that this is a bad idea by itself. Still, I can't help but think this could easily turn into a snowball effect.&lt;/p&gt;

&lt;p&gt;The book also mentions AI limitations and issues, to name a few:&lt;/p&gt;

&lt;p&gt;1) Context is king, and this has never been truer before. The importance of detail and clarity while providing the context for the LLM is huge. You cannot have it easy, though: the longer the context, the more likely the LLM is to focus on the wrong part. Also, the LLM is better at following instructions at the beginning or end of the prompt, rather than in the middle—position bias. One prompt technique is to repeat the original instruction after the user's prompt. And I thought only children were prone to remember and choose the last item they heard!&lt;/p&gt;

&lt;p&gt;2) Have you ever thought that the more you study, the less you know? While this can be the result of you being overwhelmed with the amount of available content, we also forget stuff. Fear not, as you are not alone—our LLM buddies are right there with you. The more tasks an LLM learns, the more prone it is to catastrophic forgetting, so its performance basically drops on earlier tasks. This might explain why I'm not so good at solving second-degree equations anymore. Maybe I'm an LLM.&lt;/p&gt;

&lt;p&gt;3) Ok, maybe I'm not an LLM. People are already worried that we may soon run out of content to train LLMs. Yes, LLMs are consuming publicly available information faster than it's being produced. Synthetic data—data produced by AI itself—is being used more and more to train LLMs, and this is helpful if used cautiously. Synthetic data mimics real data, so the performance achieved might be superficial. A model that gets trained with synthetic data from another model might know how to give a direct answer to a query but might not know how to explain why or how. But do not think that it will admit its lack of knowledge that easily; if you ask it for an explanation, it's likely to hallucinate one. This reminds me of The Black Swan, in which it's stated that when you ask a brain hemisphere of a split-brain patient to perform an action and then ask his other hemisphere for an explanation, the patient would usually come up with a senseless reason for his action. Anyhow, there are studies that correlate the usage of synthetic data with model underperformance. I bet this would result in such a great codebase!&lt;/p&gt;

&lt;p&gt;It's important to understand what AI brings to the table and its implications and limitations. The book does a sound explanation of that. These limitations highlight the challenges we face with AI today, which brings us to the broader discussion around the main issues of AI in software development. As many know, AI is probabilistic in nature. Have you ever seen that "your chances are low, but never zero" meme? That is the main AI motto! Anything with a non-zero probability, no matter how wrong, can be generated by AI. &lt;/p&gt;

&lt;p&gt;One issue I have experienced with AI so far is that it tends to reach conclusions without having factual consistency or sources to back its claim up. Just the other day I was questioning ChatGPT about which tag to use in an API I was consuming, and it generated an answer without any reservations. When I questioned the sources, it apologized and said it had no specific source for the claim, but that it was based on general knowledge. Turns out its answer was wrong after all, of course! In AI's defense, I must say people also claim stuff without sources to back their claims. The difference is that I don't generally see people apologizing and admitting that. So point for AI, I guess?&lt;/p&gt;

&lt;p&gt;To me, that's a pretty major issue. Before I actually questioned it, it made no effort to make it clear that it was but a guess. This is also when it's easier to exploit some AI biases. I have found that AI has a tendency for confirmation bias, in which it tends to agree with the user's idea—try asking it some advantages of breaking a bone! When it lacks factual consistency, you can easily swerve the AI's conclusion, a lot of the time resorting to made-up stuff as well! It's almost like a debate between two political parties, except for the swerving.&lt;/p&gt;

&lt;p&gt;Sadly, wild LinkedIn coaches are out there to get you! By the way, they have 2 trending topics these days:&lt;/p&gt;

&lt;p&gt;1) Good software is delivered software, and good design and architecture are meaningless. My fingers itch to dive deeper into this topic. At the risk of getting off topic, I must say that these people probably do not understand what good architecture is or have never had to deal with a rushed product as a developer. Probably both. Rushed products almost always generate way more cost in the long run than revenue in the short term.&lt;/p&gt;

&lt;p&gt;2) AI is pretty good at coding, and it will replace a fair share of developers in the near future. The coach gets a bonus point for each of the following:&lt;/p&gt;

&lt;p&gt;2.1) He says he has developed a production-ready software in a week or less.&lt;/p&gt;

&lt;p&gt;2.2) By himself. &lt;/p&gt;

&lt;p&gt;2.3) Not knowing how to code.&lt;/p&gt;

&lt;p&gt;I'll not discuss what the future holds, but at the very least, someone with such an opinion and I have way different standards for quality. Good code demands thought and creativity. If your first thought when creating a new service or feature is to create an MVC-like structure, then you and I probably have different standards for quality as well. I'm not saying you should not use AI, but in my experience, it has been useful for boilerplate and some algorithms, mostly—please, don't ask it to design a good domain model.&lt;/p&gt;

&lt;p&gt;"You were not able to generate good code for more complex tasks? The issue is you and your prompt," replies the coach. Except you paste a snippet of code for it to analyze, and it suggests an absurd change that clearly makes no sense at all as it breaks the whole class—believe me, this has happened.&lt;/p&gt;

&lt;p&gt;Once, I saw such a preacher arguing with a bunch of people how developers are no longer needed. Then he proceeded to display this "fairly complex" app—his own assessment—he had built. A horrendous interface with a few buttons that did nothing more than to calculate a few things. I guess it's needless to say this was a product guy and had no software engineering background whatsoever.&lt;/p&gt;

&lt;p&gt;What makes the whole situation worse is that people who talk like that are either deluded or want to delude you. Which one is which? Just check their profile: if they say they are AI specialists, they are deluded; if they say they own an AI product, they are out to delude you.&lt;/p&gt;

&lt;p&gt;Good output requires good input, or as data people love to say, garbage in, garbage out. A large set of an LLM's training data is composed of coding samples. It's possible to make an LLM output better code—again, I would forget domain design here—but you yourself have to be knowledgeable on what good code is. AI needs guidance for good output, and this requires critical thinking skills. At risk of offending people, it seems this is generally a lacking skill.&lt;/p&gt;

&lt;p&gt;Sadly, service God classes are very common these days. So you can imagine there is no lack of such examples for training AI. On more than one occasion, I have seen AI demonstrate theoretical knowledge and be unable to apply that in practice. On one such occasion, I was discussing software anti-patterns, and ChatGPT was adamant on saying that anemic domain models are problematic. When I asked it for good examples, it generated anemic models. While tring to refine the code, it generated meaningless examples.&lt;/p&gt;

&lt;p&gt;My conclusion? We are doomed; AI will replace humans. I mean, it already behaves as humans do: full of biases, judgmental, spitting claims without backing information, losing focus the longer you talk to them, forgetting stuff, etc. /sarcasm&lt;/p&gt;

&lt;p&gt;Ok, a useful conclusion: This is a great book to start your AI engineering roadmap—you might want to start with some basic ML books first so you find the ML parts a bit less tedious, but that's not needed. The book is dense, and it really captivates you at times. Overall, you'll get enough knowledge to be able to refine your AI applications and will have the understanding to seek more practical information sources to implement techniques such as RAG. My remarks about AI are not meant to discourage its use; I believe it has the potential to create new types of applications never seen before. AI is as useful as the user makes it. A more skilled user, with better critical thinking skills, is prone to having better results. Still, I believe it's important that people realize LLMs' limitations and that it's not that easy to fit an LLM to your use case. LLMs are not a miracle, and their output is not to be trusted as a source of truth. Obviously, neither is this review/my opinion.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>openai</category>
    </item>
    <item>
      <title>[en-UN] A multipurpose microservices' chassis pattern implementation for Spring Boot</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Fri, 23 Feb 2024 01:29:36 +0000</pubDate>
      <link>https://dev.to/renatolsjf/en-un-a-multipurpose-microservices-chassis-pattern-implementation-for-spring-boot-493a</link>
      <guid>https://dev.to/renatolsjf/en-un-a-multipurpose-microservices-chassis-pattern-implementation-for-spring-boot-493a</guid>
      <description>&lt;h1&gt;
  
  
  Repositories
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;project: &lt;a href="https://github.com/renatols-jf/spboot-chassis"&gt;spboot-chassis&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;demo: &lt;a href="https://github.com/renatols-jf/spboot-chassis-demo"&gt;spboot-chassis-demo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Discalimer
&lt;/h1&gt;

&lt;p&gt;This project was and is being developed in my free time. This means I'll do my best to solve any issues, but a few issues are to be expected. This also means there is a lot to be done, and a lot that could be better. That being said, this is currently in use in a handful of professional microservices without any issues. I'll continue to use it as it evolves, and I don't expect any downsides to it. Be it as it may, a test suite is needed. Until a comprehensive set of tests is written, this project's version will be labeled as less than 1.0.0.&lt;/p&gt;

&lt;p&gt;Also, this project does not currently support NIO as it uses ThreadLocal to control the request context. A simple solution is to add methods to take a snapshot of the context and reinitialize after the NIO code. A more automatic solution would require greater effort. Be it as it may, the snapshot is also not supported yet.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is this project?
&lt;/h1&gt;

&lt;p&gt;This is an implementation of a microservice's chassis pattern for spring boot applications. It deals with a few common concerns for distributed (or not so distributed) applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Metrics and monitoring&lt;/li&gt;
&lt;li&gt;Stamp coupling&lt;/li&gt;
&lt;li&gt;Data validation&lt;/li&gt;
&lt;li&gt;Data transformation&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Motivation
&lt;/h1&gt;

&lt;p&gt;I created this project based on the experiences I had while developing microservices in the last couple of years or so, and also based on the reading I've done so far. It's not uncommon for application needs to be ignored in favor of domain behavior, and the little code that exists is generally duplicated in many places. Also, I have seen my fair share of anemic models, and while I understand it's never so simple to get rid of data holders, I strive to use them the least possible.&lt;/p&gt;

&lt;p&gt;The whole framework is based on the idea that all that happens within an application request is connected to its current context. Data validation is not static, and neither is data transformation. The current context is available to the application anytime and can be used to transform and validate data, among other things.&lt;/p&gt;

&lt;h1&gt;
  
  
  Usage
&lt;/h1&gt;

&lt;p&gt;To use this project, you need to update your pom.xml if using Maven&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.github.renatols-jf&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spboot-chassis&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.0.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or your build.gradle if using Gradle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation group: 'io.github.renatols-jf', name: 'spboot-chassis', version: '0.0.2'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a Spring Boot framework, and it will need to access Spring-managed objects and dependency injection. A new interaction mechanism will be provided in future releases, but currently, this project's main package needs to be scanned by Spring. This is done by adding the &lt;code&gt;ComponentScan&lt;/code&gt; annotation to the application, as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootApplication
@ComponentScan("io.github.renatolsjf")
public class DemoApplication {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@SpringBootApplication&lt;/code&gt; adds &lt;code&gt;@ComponentScan&lt;/code&gt; for the application's main package, but I've seen issues with this when adding another package. So you might need to add it again, as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootApplication
@ComponentScan("com.example.demo")
@ComponentScan("io.github.renatolsjf")
public class DemoApplication {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Request
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/request/Request.java"&gt;Request&lt;/a&gt;is a unit of behavior. Everything that happens in the application should be within a request. It automatically provides a means to log the request, update application metrics, and some other useful application behavior. The idea is to think of a request as a unit of processing, no matter the entry point. An HTTPS call should be mapped to a request, just as a message consumed from a queue.&lt;/p&gt;

&lt;p&gt;Request is an abstract class and needs to be extended to provide any functionality. The idea is for the superclass to control application behavior, while the subclass controls domain behavior.&lt;/p&gt;

&lt;p&gt;The domain logic is to be implemented in the method &lt;code&gt;doProcess()&lt;/code&gt; - it should &lt;strong&gt;NEVER&lt;/strong&gt; be called directly, instead, &lt;code&gt;process()&lt;/code&gt; should. At this point, you might want to access Spring-managed objects, such as services. There are two ways of doing so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;this.requestResource(MyService.class)&lt;/code&gt; in which you provide the class you are expecting. This is a syntatic sugar available only inside requests.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AppRegistry.getResource(MyService.class)&lt;/code&gt; in which you provide the class you are expecting. This can be called anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a request is finished, it must set its &lt;code&gt;RequestOutcome&lt;/code&gt;. If no exception is thrown, the request is deemed successful, and &lt;code&gt;RequestOutcome.SUCCESS&lt;/code&gt; is initialized automatically. There are two methods used to realize the final outcome in the case of an exception:&lt;code&gt;Request#resolveError&lt;/code&gt; and &lt;code&gt;Request#doResolveError&lt;/code&gt; - both receive a &lt;code&gt;Throwable&lt;/code&gt; as a parameter.&lt;/p&gt;

&lt;p&gt;The idea is to identify the outcome based on the exception thrown. &lt;code&gt;Request#resolveError&lt;/code&gt; is called first. It checks if the &lt;code&gt;Throwable&lt;/code&gt; is an instance of &lt;code&gt;ValidationException&lt;/code&gt;, in which case it sets the outcome as &lt;code&gt;RequestOutcome.CLIENT_ERROR&lt;/code&gt;. If it is not, it defers the decision to&lt;code&gt;Request#doResolveError&lt;/code&gt;, which is abstract and must be implemented. In the case that&lt;code&gt;Request#doResolveError&lt;/code&gt; return nulls (this &lt;strong&gt;SHOULD NOT&lt;/strong&gt; happen), &lt;code&gt;Request#resolveError&lt;/code&gt;sets the outcome as &lt;code&gt;RequestOutcome.SERVER_ERROR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are a few request constructors available, but we will approach only the most complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Request(String operation, String transactionId, String correlationId, List&amp;lt;String&amp;gt; projection,
                   Map&amp;lt;String, String&amp;gt; requestContextEntries)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  operation
&lt;/h3&gt;

&lt;p&gt;Operation is the name given to the request or unit of behavior. While we might have some more generic request implementations, it is expected that the subclass initializes this value without it actually being one of its constructor parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public CalculateMarginRequest(String transactionId, String correlationId, List&amp;lt;String&amp;gt; projection,
                   Map&amp;lt;String, String&amp;gt; requestContextEntries) extends Request {
    super("CALCULATE_MARGIN", transactionId, correlationId, projection, requestContextEntries);                  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This operation name will be the same used elsewhere to assert validations and transformations, so it's probably a good idea to use a constant.&lt;/p&gt;

&lt;h3&gt;
  
  
  transactionId
&lt;/h3&gt;

&lt;p&gt;A unique identifier given to the transaction. The idea is to serve as an identifier so a transaction can be traced through the various services. If a null transactionId is provided, a UUID4 will be created.&lt;/p&gt;

&lt;h3&gt;
  
  
  correlationId
&lt;/h3&gt;

&lt;p&gt;An external identifier given by an application outside the scope of the internal services. It enables the identification of every transaction or request served with that corelationId.&lt;/p&gt;

&lt;h3&gt;
  
  
  projection
&lt;/h3&gt;

&lt;p&gt;Projection exists as a means to tackle stamp coupling. There are services that generate big payloads as a response, and clients that need only a specific portion of that response. This could lead to traffic overcharges, the need to map unnecessary fields, among other issues.&lt;/p&gt;

&lt;p&gt;Each request supports a list of field names that will be automatically filtered before the response is given. Nestesd fields are represented with dots. So, if a request returns the following json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "aField":"aValue",
   "anotherField":true,
   "anObject":{
      "f1":12,
      "f2":null
   },
   "aList":[
      {
         "f3":false,
         "f4":"Andrew"
      },
      {
         "f3":true,
         "f4":"Ryan"
      }
   ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the projection &lt;code&gt;["aField", "anObject.f2", "aList.f3"]&lt;/code&gt; will yield:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "aField":"aValue",
   "anObject":{
      "f2":null
   },
   "aList":[
      {
         "f3":false
      },
      {
         "f3":true
      }
   ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  requestContextEntries
&lt;/h3&gt;

&lt;p&gt;We might want to log information that is not available to the current service, but is used to identify messages in the service chain. Initializing this map will make every entry* in it be logged with each message during the request duration. The entries can also be supplied as a &lt;code&gt;String&lt;/code&gt;, separating key from value using &lt;code&gt;:&lt;/code&gt; and separating entries using &lt;code&gt;;&lt;/code&gt;, as in &lt;code&gt;aKey:aValue;anotherKey:anotherValue&lt;/code&gt;. It's highly recommended to use&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/request/EntryResolver.java"&gt;EntryResolver&lt;/a&gt;to do conversions if needed, though.&lt;/p&gt;

&lt;p&gt;*Entries are logged in their own fields; exportation of fields other than message&lt;br&gt;
depends on the logging configuration.&lt;/p&gt;
&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/Context.java"&gt;Context&lt;/a&gt; is the source of information for the current processing/request. It stores information like the operation in execution and the transactionId. It is used by the framework to initiate transformations and validations. It also stores information like elapsed time by type, which will be treated later in this document.&lt;/p&gt;

&lt;p&gt;It is initialized automatically as soon as a request object is created and it's detroyed as soon as a request finishes. A context can be obtained anywhere calling &lt;code&gt;Context.forRequest()&lt;/code&gt;. Inside a request, a context is also an attribute and can be accessed directly by calling &lt;code&gt;context&lt;/code&gt;. As a rule of thumb, we don't want to initialize the context manually elsewhere, so calling &lt;code&gt;Context.initialize&lt;/code&gt; outside of a request will result in an error. You also cannot call it manually inside a request, as it will result in an error since the context already exists.&lt;/p&gt;

&lt;p&gt;If you absolutely need to create a context outside of a request, the class in which you will be doing so needs to be annotated with&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/ContextCreator.java"&gt;@ContextCreator&lt;/a&gt;. This will enable a context to be created. If a context already exists, an error will still be thrown.&lt;/p&gt;
&lt;h2&gt;
  
  
  Logging
&lt;/h2&gt;

&lt;p&gt;Logging can be done by requesting an &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/ApplicationLogger.java"&gt;ApplicationLogger&lt;/a&gt; from the context: &lt;code&gt;ApplicationLogger logger = Context.forRequest().createLogger()&lt;/code&gt;. With an ApplicationLogger instance, you can use the default logging levels to log information, as is: &lt;code&gt;logger.info(message, param1, param2).log()&lt;/code&gt;. A few observations are in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Any log level provides two arguments: a message and an object varargs. Any varargs present will replace &lt;code&gt;{}&lt;/code&gt; inside the message.&lt;code&gt;logger.info("{} is greater than {}", number2, number1).log&lt;/code&gt;will result in "2 is greater than 1" being logged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ApplicationLogger#error&lt;/code&gt; also provides an exception argument. This will result in the stack trace being logged. Currently, framework exceptions are logged with their stack traces. A future release will allow this to be disabled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The logging level methods do not actually log the information but rather wait for the &lt;code&gt;log()&lt;/code&gt; call. That is because we might want to add some extra information to the log. In a future release, the need of &lt;code&gt;log()&lt;/code&gt; will be removed when extra information is not needed. Besides the fields present in the context, information related only to that message can also be logged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Context.forRequest().createLogger().log(message).attach("name", "Andrew").log()&lt;/code&gt;will create a new field named "name" only for this message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Context.forRequest().createLogger().log(message).attachMap(aMap).log()&lt;/code&gt;will create as many new fields as keys available in the map. This method also supports a String varargs to log only the desired fields.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Context.forRequest().createLogger().log(message).attachObject(anObject).log()&lt;/code&gt;will create as many fields as are available in the object. Again, you could filter the object providing only the keys you desire to log, but there is another approach for objects. You can annotate any field in a Class that you don't want to log as &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/data/Classified.java"&gt;@Classified&lt;/a&gt;. This will create automatic transformations that will be applied to the field before exportation. You need to provide a &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/data/cypher/ClassifiedCypher.java"&gt;ClassifiedCypher&lt;/a&gt; implementation to the annotation. This strategy is used by the request as it logs its information. You can create yours or use one of the available (if none is specified,&lt;code&gt;IgnoringCypher&lt;/code&gt; will be used):

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/data/cypher/HiddenClassifiedCypher.java"&gt;HiddenClassifiedCypher&lt;/a&gt; which will print only if the field has a value or not.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/context/data/cypher/IgnoringCypher.java"&gt;IgnoringCypher&lt;/a&gt; which will completely ignore that field.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fields added via &lt;code&gt;attach&lt;/code&gt; will &lt;strong&gt;NOT&lt;/strong&gt; be exported in their own fields. Instead, they will be grouped in a field called &lt;code&gt;context&lt;/code&gt;. A future release will provide a configuration to change this behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        Context.forRequest().withRequestContextEntry("fixedField", "Present in all messages!")
                .createLogger()
                .info("A message")
                .attach("aField", "aValue")
                .attach("anotherField", "anotherValue")
                .log();

        Context.forRequest()
                .createLogger()
                .info("A  second message")
                .attach("aThridField", "aThirdValue")
                .log();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"@timestamp":"2024-02-21T15:25:36.382-03:00","message":"A message","logger_name":"com.example.demo.DemoRequest","level":"INFO","context":"{\"anotherField\":\"anotherValue\",\"aField\":\"aValue\"}","fixedField":"Present in all messages!","operationTimes":"{internal=8, total=8}","operation":"DEMO_OPERATION","transactionId":"c52fa6c6-a5d9-4a39-961c-f832f232da57","elapsedTime":"8","application":"demo-application"}
{"@timestamp":"2024-02-21T15:25:36.382-03:00","message":"A  second message","logger_name":"com.example.demo.DemoRequest","level":"INFO","context":"{\"aThridField\":\"aThirdValue\"}","fixedField":"Present in all messages!","operationTimes":"{internal=8, total=8}","operation":"DEMO_OPERATION","transactionId":"c52fa6c6-a5d9-4a39-961c-f832f232da57","elapsedTime":"8","application":"demo-application"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rendering
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/Media.java"&gt;Media&lt;/a&gt; and &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/Renderable.java"&gt;Renderable&lt;/a&gt; are the main components of rendering. Every request terminates with render information, even if there is nothing to render.  One of the main goals of the rendering framework is to avoid the creation of DTOs. You should terminate &lt;code&gt;Request#doProcess&lt;/code&gt;with one of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Media.ofRenderable(aRenderable)&lt;/code&gt; providing a single instance of &lt;code&gt;Renderable&lt;/code&gt; to render.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Media.ofCollection(aRenderableCollection)&lt;/code&gt; providing a collection of &lt;code&gt;Renderables&lt;/code&gt; to render.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Media.empty()&lt;/code&gt; to render nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Renderable
&lt;/h3&gt;

&lt;p&gt;Renderable denotes an object that can be rendered, it governs what and how is rendered or exported. There two ways to mark a class as renderable:implementing &lt;code&gt;Renderable&lt;/code&gt; or implementing &lt;code&gt;FieldRenderable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Renderable&lt;/code&gt; provides a render method in which the desired information is exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.rendering.Media;
import io.github.renatolsjf.chassis.rendering.Renderable;

public class Greeter implements Renderable {

    private final String name;

    public Greeter(String name) {
        this.name = name;
    }

    public String greet() {
        return "Hi! My name is: " + this.name;
    }

    @Override
    public Media render(Media media) {
        return media.print(name, this.name)
                .print("greeting", this.greet());
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rendering the above class (calling &lt;code&gt;Media.ofRenderable(new Greeter("Andrew")).render()&lt;/code&gt;) will yield:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "name": "Andrew",
   "greeting": "Hi! My name is Andrew"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also render nested renderables of collections of renderables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.rendering.Media;
import io.github.renatolsjf.chassis.rendering.Renderable;

import java.util.ArrayList;
import java.util.List;

public class Greeter implements Renderable {

    private final String name;
    List&amp;lt;Echo&amp;gt; echoes = new ArrayList&amp;lt;&amp;gt;();

    public Greeter(String name) {
        this.name = name;
        this.echoes.add(new Echo());
        this.echoes.add(new Echo());
    }

    public String greet() {
        return "My name is: " + this.name;
    }

    @Override
    public Media render(Media media) {
        return media.print(name, this.name)
                .print("greeting", this.greet())
                .forkCollection("echoes", this.echoes);
    }

}

class Echo implements Renderable {
    @Override
    public Media render(Media media) {
        return media.print("echo", "what they said");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rendering the above class (calling &lt;code&gt;Media.ofRenderable(new Greeter("Andrew")).render()&lt;/code&gt;) will yield:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "name":"Andrew",
   "greeting":"Hi! My name is Andrew",
   "echoes":[
      {
         "echo":"what they said"
      },
      {
         "echo":"what they said"
      }
   ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single &lt;code&gt;Renderable&lt;/code&gt; can be nested using &lt;code&gt;Media#forkRenderable&lt;/code&gt; instead of &lt;code&gt;Media#forkCollection&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  FieldRenderable
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;FieldRenderable&lt;/code&gt; does not require an implementation for &lt;code&gt;Renderable#render&lt;/code&gt;. It walks the object inheritance tree and prints each class' fields up to &lt;code&gt;Object.class&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The rendering can be customized using &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/config/RenderConfig.java"&gt;@RenderConfig&lt;/a&gt;. &lt;code&gt;RenderConfig&lt;/code&gt; supports the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;operation&lt;/code&gt;: indicates to which operations this configuration applies. It can be empty, a single operation, or multiple operations. A field can have more than one &lt;code&gt;RenderConfig&lt;/code&gt;. If that's the case, the most suitable configuration will be applied. That is the first configuration which has the current operation listed or a configuration which has no operations configured.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;policy&lt;/code&gt; (&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/config/RenderPolicy.java"&gt;@RenderPolicy&lt;/a&gt;): indicates whether the field will be rendered or ignored. This can be configured via&lt;code&gt;RenderPolicy.Policy&lt;/code&gt; as in &lt;code&gt;@RenderConfig(policy = @RenderPolicy(RenderPolicy.Policy.RENDER))&lt;/code&gt;or &lt;code&gt;@RenderConfig(policy = @RenderPolicy(RenderPolicy.Policy.IGNORE))&lt;/code&gt;. The default behavior is to render.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alias&lt;/code&gt;(&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/config/RenderAlias.java"&gt;@RenderAlias&lt;/a&gt;): provides an alias for the current field:&lt;code&gt;@RenderConfig(alias = @RenderAlias("anAlias"))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transformer&lt;/code&gt;(&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/config/RenderTransform.java"&gt;@RenderTransform&lt;/a&gt;): provides an implementation of &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/config/RenderTransformer.java"&gt;RenderTransformer&lt;/a&gt; that will transform the field value (&lt;code&gt;RenderTransformer&lt;/code&gt; &lt;strong&gt;MUST&lt;/strong&gt; have a public no args constructor and be a public class):&lt;code&gt;@RenderConfig(transformer = @RenderTransform(MyTransformer.class))&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, there is no way to print nested renderables or a collection of nested renderables - this will be add in a future release. They should be configured not to be printed or some sort of workaround should be done. You can override the &lt;code&gt;FieldRenderable&lt;/code&gt; default behavior as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.rendering.FieldRenderable;
import io.github.renatolsjf.chassis.rendering.Media;
import io.github.renatolsjf.chassis.rendering.Renderable;
import io.github.renatolsjf.chassis.rendering.config.RenderConfig;
import io.github.renatolsjf.chassis.rendering.config.RenderPolicy;
import io.github.renatolsjf.chassis.rendering.config.RenderTransform;

import java.util.ArrayList;
import java.util.List;

public class Greeter implements FieldRenderable {

    private final String name;
    @RenderConfig(policy = @RenderPolicy(RenderPolicy.Policy.IGNORE))
    List&amp;lt;Echo&amp;gt; echoes = new ArrayList&amp;lt;&amp;gt;();

    public Greeter(String name) {
        this.name = name;
        this.echoes.add(new Echo());
        this.echoes.add(new Echo());
    }

    public String greet() {
        return "My name is: " + this.name;
    }

    @Override
    public Media render(Media media) {
        return FieldRenderable.super.render(media)
                .forkCollection("echoes", this.echoes);
    }

}

class Echo implements Renderable {
    @Override
    public Media render(Media media) {
        return media.print("echo", "what they said");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rendering the above class (calling &lt;code&gt;Media.ofRenderable(new Greeter("Andrew")).render()&lt;/code&gt;) will yield:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "name":"Andrew",
   "echoes":[
      {
         "echo":"what they said"
      },
      {
         "echo":"what they said"
      }
   ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Media
&lt;/h3&gt;

&lt;p&gt;It's the means by which the information will be recorded. You never create a Media object, instead you call &lt;code&gt;Media.ofRenderable&lt;/code&gt;, &lt;code&gt;Media.ofCollection&lt;/code&gt;, or&lt;code&gt;Media.empty&lt;/code&gt;. A call to &lt;code&gt;Media#render&lt;/code&gt; actually exports the object information.&lt;br&gt;
This is an area that can be improved upon. Render returns an &lt;code&gt;Object&lt;/code&gt;, which is either a &lt;code&gt;Map&lt;/code&gt; for single instances or a &lt;code&gt;List&lt;/code&gt; for collections. Spring Boot will return this without any issues, but a more powerful abstraction is a good idea.&lt;/p&gt;
&lt;h3&gt;
  
  
  Transforming
&lt;/h3&gt;

&lt;p&gt;Transforming is the means by which an output is transformed into another. This is &lt;strong&gt;NOT&lt;/strong&gt;the same as &lt;code&gt;@RenderTransform&lt;/code&gt; from &lt;code&gt;@RenderConfig&lt;/code&gt;. &lt;code&gt;@RenderTransform&lt;/code&gt; applies to a single field, while this operation applies to the whole data. It can be called manually or initialized automatically according to the context. Currently, the only context transformation available is the projection one.&lt;/p&gt;

&lt;p&gt;Transformations can be done using &lt;br&gt;
&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/transforming/TransformingPath.java"&gt;TransformingPath&lt;/a&gt;, an implementation of &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/MediaTransformer.java"&gt;MediaTransformer&lt;/a&gt;, and &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/rendering/MediaContent.java"&gt;MediaContent&lt;/a&gt;. Currently, very little support exists for this type of transformation. This is a part of the framework that needs to evolve, so I'll not write more information on this topic for now. You are probably better off not using it at the moment.&lt;/p&gt;
&lt;h2&gt;
  
  
  Validation
&lt;/h2&gt;

&lt;p&gt;Any class that implements &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/validation/Validatable.java"&gt;Validatable&lt;/a&gt; can be validated simply by calling &lt;code&gt;myValidatableInstance.validate()&lt;/code&gt;. This will trigger an object lookup for any nested &lt;code&gt;Validatable&lt;/code&gt; objects to validate all of them.&lt;/p&gt;

&lt;p&gt;To configure validations, you have to annotate &lt;code&gt;Validatable&lt;/code&gt; fields or methods with &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/validation/annotation/Validation.java"&gt;@Validation&lt;/a&gt;. A field or a method can have multiple validations configured. A &lt;code&gt;Validation&lt;/code&gt; accepts the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;operation&lt;/code&gt;: indicates to which operations this validation applies. It can be empty, a single operation, or multiple operations. If a field or method has more than one &lt;code&gt;Validation&lt;/code&gt;, all of those that have no operation configured or that have the current operation in its operation's list, will be applied.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;nullable&lt;/code&gt;(&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/validation/annotation/Nullable.java"&gt;@Nullable&lt;/a&gt;): indicates whether a field or return of method can be null. Accepted values are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CAN_BE_NULL - @Validation(nullable = @Nullable(Nullable.NullableType.CAN_BE_NULL))&lt;/code&gt;:in which the value can either be null or not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CANT_BE_NULL - @Validation(nullable = @Nullable(Nullable.NullableType.CANT_BE_NULL))&lt;/code&gt;:in which the value cannot be null.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MUST_BE_NULL - @Validation(nullable = @Nullable(Nullable.NullableType.CAN_BE_NULL))&lt;/code&gt;:in which the value must be null.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;minimum&lt;/code&gt;(&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/validation/annotation/Minimum.java"&gt;@Minimum&lt;/a&gt;): indicates the minimum value of the field - &lt;code&gt;@Validation(minimum = @Minimum(10))&lt;/code&gt;. Currently, a minimum value of 0 disables the validation. In a future release, this will be changed to &lt;code&gt;Integer.MIN_VALUE&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;oneOf&lt;/code&gt; (&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/validation/annotation/OneOf.java"&gt;@OneOf&lt;/a&gt;): indicates that a field's value must be equal to one of the provided values -&lt;code&gt;@Validation(oneOf = @OneOf({"Ryan", "Andrew"}))&lt;/code&gt;. Currently, it supports only&lt;code&gt;String&lt;/code&gt; values, but it will be expanded in a future release.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pattern&lt;/code&gt;(&lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/validation/annotation/Pattern.java"&gt;@Pattern&lt;/a&gt;): indicates that a &lt;code&gt;String&lt;/code&gt; must match a provided Regex -&lt;code&gt;@Validation(pattern = @Pattern("^(?!000|666)[0-8][0-9]{2}-(?!00)[0-9]{2}-(?!0000)[0-9]{4}$"))&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every validation type also accepts a message parameter, which will override the default error message in case of a validation error.&lt;/p&gt;
&lt;h2&gt;
  
  
  API integrations - HTTP(s) calls
&lt;/h2&gt;

&lt;p&gt;Each HTTP request should be made using the &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/integration/RestOperation.java"&gt;RestOperation&lt;/a&gt; class. To create a &lt;code&gt;RestOperation&lt;/code&gt; you should call &lt;code&gt;RestOperation#create&lt;/code&gt;providing the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;group&lt;/code&gt;: It's used to identify to whom the service being called belongs. It's the first of a three-layered identification. I generally use the company or team responsible for the service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;service&lt;/code&gt;: It's used to identify to whom the service being called belongs to. It's the second of a three-layered identification. I generally use the actual service name being called.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;operation&lt;/code&gt;: It's used to identify to whom the service being called belongs to. It's the third of a three-layered identification. I generally use a name for the operation being requested, e.g., Authorization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;uri&lt;/code&gt;: self-explanatory; the URI being called.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;headers&lt;/code&gt;: A &lt;code&gt;Map&amp;lt;String, String&amp;gt;&lt;/code&gt; with header information. Can be null.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;body&lt;/code&gt;: A &lt;code&gt;Map&amp;lt;String, Object&amp;gt;&lt;/code&gt; with body information. Can be null.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;HttpMethod&lt;/code&gt;: The desired HTTP method.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;RestOperation#create&lt;/code&gt; can also be replaced with &lt;code&gt;RestOperation#get&lt;/code&gt;, &lt;code&gt;RestOperation#post&lt;/code&gt;, &lt;code&gt;RestOperation#patch&lt;/code&gt;, &lt;code&gt;RestOperation#put&lt;/code&gt; or &lt;code&gt;RestOperation#delete&lt;/code&gt; - these do not need the &lt;code&gt;HttpMethod&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;To make the HTTP call, simply call &lt;code&gt;RestOperation#call&lt;/code&gt;. Two implementations are available: one that accepts a return type and one that accepts a return type and an error type. If no return type is expected, a null value can be passed. If an spefic error type is not expected, using the &lt;code&gt;RestOperation#call&lt;/code&gt; without the error type will result in errors being initialized in a simple &lt;code&gt;Map&lt;/code&gt;. In the event of an error, either a &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/integration/ClientErrorOperationException.java"&gt;ClientErrorOperationException&lt;/a&gt; or a &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/integration/ServerErrorOperationException.java"&gt;ServerErrorOperationException&lt;/a&gt; will be thrown. Both are implementations of &lt;code&gt;StatusRestOperationException&lt;/code&gt;, which provides &lt;code&gt;getBody()&lt;/code&gt; to get the parsed error.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;RestOperation&lt;/code&gt; provides automatic logging and metrics creation for the call. These will be configurable in a future release.&lt;/p&gt;
&lt;h2&gt;
  
  
  Monitoring
&lt;/h2&gt;

&lt;p&gt;This framework exports metrics to Prometheus automatically using the Spring actuator. It provides a facade for metrics, which will reflect in the Spring/Micrometer &lt;code&gt;MeterRegistry&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Metric types and creation
&lt;/h3&gt;

&lt;p&gt;Metrics are created with the help of &lt;code&gt;MetricRegistry&lt;/code&gt; - not  the same as &lt;code&gt;MeterRegistry&lt;/code&gt; from Spring/Micrometer. There is no need to store references to the created metrics. Each time you try to build a &lt;code&gt;Metric&lt;/code&gt;, the &lt;code&gt;MetricRegistry&lt;/code&gt; will either create a new one or return an existing &lt;code&gt;Metric&lt;/code&gt; in case it's the same type and has the same name and same tags as the one requested. Currently, there are 4 metrics that can be created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Counter&lt;/code&gt;: A counter, as the name says, counts something. As long as the application is running, its value never resets.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.Chassis;

public class MetricDemo {

    public MetricDemo() {

        /*
        Increases the value by 1
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_counter")
                .withTag("aTagName", "aTagValue")
                .buildCounter()
                .inc();
        /*
        Increases the value by the desired amount
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_counter")
                .withTag("aTagName", "aTagValue")
                .buildCounter()
                .inc(2d);

        /*
        Exported to Prometheus as:
        a_counter_total{aTagName="aTagValue",} 3.0
         */
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Gauge&lt;/code&gt;: A gauge stores a value that can be changed as desired.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.Chassis;

public class MetricDemo {
    public MetricDemo() {
        /*
        Increases the value by 1
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_gauge")
                .withTag("aTagName", "aTagValue")
                .buildGauge()
                .inc();
        /*
        Increases the value by the desired amount
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_gauge")
                .withTag("aTagName", "aTagValue")
                .buildGauge()
                .inc(2d);

        /*
        Decreases the value by the desired amount
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_gauge")
                .withTag("aTagName", "aTagValue")
                .buildGauge()
                .dec();

        /*
        Decreases the value by the desired amount
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_gauge")
                .withTag("aTagName", "aTagValue")
                .buildGauge()
                .dec(2d);

        /*
        Sets the value to the current timestamp
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_gauge")
                .withTag("aTagName", "aTagValue")
                .buildGauge()
                .setToCurrentTime();

        /*
        Sets the value to the desired amount
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_gauge")
                .withTag("aTagName", "aTagValue")
                .buildGauge()
                .set(2d);

        /*
        Expord to Prometheus as:
        a_gauge{aTagName="aTagValue",} 2.0
         */

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TrackingGauge&lt;/code&gt;: A &lt;code&gt;TrackingGauge&lt;/code&gt; is fundamentally the same as a &lt;code&gt;Gauge&lt;/code&gt;. It differs in the way the measurement is done. You don't change its value manually. Instead, it tracks an &lt;code&gt;ObservableTask&lt;/code&gt; object.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.Chassis;
import io.github.renatolsjf.chassis.monitoring.ObservableTask;

public class MetricDemo {
    public MetricDemo() {

        /*
        Tracks the desired task
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_tracking_gauge")
                .withTag("aTagName", "aTagValue")
                .buildTrackingGauge()
                .track(() -&amp;gt; 2); //or .track(new Task());

        /*
        Tracks the desired task with a WeakReference. As soon as the WeakReference is cleared
        the metric will measure 0. Since the new task is not referenced anywhere else,
        this metric will default to 0 as soon as the garbage collector runs.
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_tracking_gauge")
                .withTag("aTagName", "aTagValue")
                .buildTrackingGauge()
                .weakTrack(new Task()); //or .weakTrack(() -&amp;gt; 2);

        /*
        Exported to Prometheus as:
        a_tracking_gauge{aTagName="aTagValue",} 100.0
         */

    }
}

class Task implements ObservableTask {
    @Override
    public double getCurrentValue() {
        return 100;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Histogram&lt;/code&gt;: histogram counts distributions along a defined range. It can be used to calculate quantiles on Prometheus.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.Chassis;

public class MetricDemo {
    public MetricDemo() {

        /*
        Creates a histogram with bucket values of 0.1, 0.2, 0.5, 1, 5, and 10
         */
        Chassis.getInstance().getMetricRegistry().createBuilder("a_histogram")
                .withTag("aTagName", "aTagValue")
                .buildHistogram(.1d, .2d, .5d, 1d, 5d, 10d)
                .observe(.255d);

        /*
        Exported to Prometheus as:
        a_histogram_bucket{aTagName="aTagValue",le="0.1",} 0.0
        a_histogram_bucket{aTagName="aTagValue",le="0.2",} 0.0
        a_histogram_bucket{aTagName="aTagValue",le="0.5",} 1.0
        a_histogram_bucket{aTagName="aTagValue",le="1.0",} 1.0
        a_histogram_bucket{aTagName="aTagValue",le="5.0",} 1.0
        a_histogram_bucket{aTagName="aTagValue",le="10.0",} 1.0
        a_histogram_bucket{aTagName="aTagValue",le="+Inf",} 1.0
        a_histogram_count{aTagName="aTagValue",} 1.0
        a_histogram_sum{aTagName="aTagValue",} 0.255
         */

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Built-in metrics
&lt;/h2&gt;

&lt;p&gt;Each time a request is executed, a few pre-defined metrics are created as the operation runs. If a request should not count towards application health, as the health request itself does not, the request class should be annotated with &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/monitoring/request/HealthIgnore.java"&gt;@HealthIgnore&lt;/a&gt;. If the request does not have that annotation, the following metrics will be generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;Gauge&lt;/code&gt; called &lt;code&gt;operation_active_requests&lt;/code&gt;. It stores how many requests are running. It increases as soon as a request starts and decreases as soon as the request finishes. It uses the current &lt;code&gt;operation&lt;/code&gt; as a &lt;code&gt;tag&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;Gauge&lt;/code&gt; called &lt;code&gt;operation_health&lt;/code&gt;. It stores the current health of a given operation. It uses the current &lt;code&gt;operation&lt;/code&gt; as a &lt;code&gt;tag&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;Histogram&lt;/code&gt;, named &lt;code&gt;operation_request_time&lt;/code&gt;. It stores the time taken for each request, in milliseconds, in buckets. It uses as a &lt;code&gt;tag&lt;/code&gt;: the current &lt;code&gt;operation&lt;/code&gt;, the &lt;code&gt;outcome&lt;/code&gt;of the request (success, client_error, or server_error), and the &lt;code&gt;timer_type&lt;/code&gt;. More information on &lt;code&gt;timer_type&lt;/code&gt; is provided in Timing operations and timer classification section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If a &lt;code&gt;RestOperation&lt;/code&gt; is executed, a &lt;code&gt;Histogram&lt;/code&gt; named &lt;code&gt;integration_request_time&lt;/code&gt;. It stores the time taken for each HTTP call, in milliseconds, in buckets. It uses as a &lt;code&gt;tag&lt;/code&gt;: the &lt;code&gt;group&lt;/code&gt; for the &lt;code&gt;RestOperation&lt;/code&gt;, the &lt;code&gt;service&lt;/code&gt; for the &lt;code&gt;RestOperation&lt;/code&gt;the &lt;code&gt;operation&lt;/code&gt; for the &lt;code&gt;RestOperation&lt;/code&gt;, &lt;code&gt;outcome&lt;/code&gt; which is either an &lt;code&gt;Http Status Code&lt;/code&gt; or&lt;code&gt;connection_error&lt;/code&gt;, and &lt;code&gt;type&lt;/code&gt;, which is always &lt;code&gt;rest&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how those metrics are exported to Prometheus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;operation_health{operation="DEMO_OPERATION",} 100.0
operation_active_requests{operation="DEMO_OPERATION",} 0.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="200.0",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="500.0",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="1000.0",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="2000.0",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="5000.0",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="10000.0",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",le="+Inf",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",} 1.0
operation_request_time_bucket{operation="DEMO_OPERATION",outcome="success",timer_type="internal",} 10.0
operation_request_time_max{operation="DEMO_OPERATION",outcome="success",timer_type="internal",} 10.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="200.0",} 0.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="500.0",} 0.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="1000.0",} 1.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="2000.0",} 1.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="5000.0",} 1.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="10000.0",} 1.0
integration_request_time_bucket{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",le="+Inf",} 1.0
integration_request_time_count{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",} 1.0
integration_request_time_sum{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",} 760.0
integration_request_time_max{group="GOOGLE",operation="SEARCH",outcome="200",service="SEARCH",type="rest",} 760.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Built-in health information
&lt;/h2&gt;

&lt;p&gt;The application has a default &lt;code&gt;HealthRequest&lt;/code&gt; that exports health information in &lt;code&gt;json&lt;/code&gt;. It exports health percentage, request count, quantiles for the time taken for each type, and result count by type. It does so by each operation, and also aggregates as application information. The application health is not an average. Instead, it reflects the health of the worst operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "application":{
      "health":100.0,
      "load":{
         "requestCount":1,
         "requestTime":{
            "internal":{
               "quantiles":{
                  "0.5":13,
                  "0.95":13,
                  "0.99":13
               }
            }
         }
      },
      "result":{
         "success":1,
         "clientError":0,
         "serverError":0
      }
   },
   "operations":[
      {
         "name":"DEMO_OPERATION",
         "health":100.0,
         "load":{
            "requestCount":1,
            "requestTime":{
               "internal":{
                  "quantiles":{
                     "0.5":13,
                     "0.95":13,
                     "0.99":13
                  }
               }
            }
         },
         "result":{
            "success":1,
            "clientError":0,
            "serverError":0
         }
      }
   ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Timing operations and timer classification
&lt;/h2&gt;

&lt;p&gt;The framework enables us to classify the time taken while processing an operation. The classification is done using a simple &lt;code&gt;String&lt;/code&gt; as a &lt;code&gt;Tag&lt;/code&gt;. To measure the time for a block of code, we use &lt;a href="https://github.com/renatols-jf/spboot-chassis/blob/master/src/main/java/io/github/renatolsjf/chassis/monitoring/timing/TimedOperation.java"&gt;TimedOperation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;TimedOperation&lt;/code&gt; will record the time it took automatically to the &lt;code&gt;Context&lt;/code&gt;. You can tag the &lt;code&gt;TimedOperation&lt;/code&gt; however you like, using &lt;code&gt;new TimedOperation(myTag)&lt;/code&gt;, or using one of two pre-defined tags: http &lt;code&gt;TimedOperation.http()&lt;/code&gt;, used for HTTP calls, and db &lt;code&gt;TimedOperation.db()&lt;/code&gt;, used for database calls. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;TimedOperation.http()&lt;/code&gt; is used internally in &lt;code&gt;RestOperation&lt;/code&gt;, and &lt;code&gt;TimedOperation.db()&lt;/code&gt; has to manually wrap a database call. It's not uncommon for database calls to be automatic, having only the interface for the &lt;code&gt;Repository&lt;/code&gt; created. To avoid wrapping every call to the repository in &lt;code&gt;TimedOperation&lt;/code&gt;, you can create a delegate for such cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface DemoRepositoryDelegate extends JpaRepository&amp;lt;Demo, String&amp;gt; {
    List&amp;lt;Demo&amp;gt; findAllByStatus(Demo.Status status);
    Optional&amp;lt;Demo&amp;gt; findByAField(String aFieldValue);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import io.github.renatolsjf.chassis.monitoring.timing.TimedOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public class DemoRepository {

    @Autowired
    private DemoRepositoryDelegate demoRepositoryDelegate;

    public List&amp;lt;Demo&amp;gt; findUnfinishedDemos() {
        return TimedOperation.&amp;lt;List&amp;lt;Demo&amp;gt;&amp;gt;db()
                .execute(() -&amp;gt; this.demoRepositoryDelegate.findAllByStatus(Demo.Status.UNFINISHED));
    }

    public Optional&amp;lt;Demo&amp;gt; findByAField(String aFieldValue) {
        return TimedOperation.&amp;lt;Optional&amp;lt;Demo&amp;gt;&amp;gt;db()
                .execute(() -&amp;gt; this.demoRepositoryDelegate.findByAField(aFieldValue));
    }

    public void saveDemo(Demo demoData) {
        TimedOperation.db().run(() -&amp;gt; this.demoRepositoryDelegate.save(demoData));
    }

    public void deleteDemo(Demo demoData) {
        TimedOperation.db().run(() -&amp;gt; this.demoRepositoryDelegate.delete(demoData));
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A timed operation can be executed in two ways: &lt;code&gt;run&lt;/code&gt; and &lt;code&gt;execute&lt;/code&gt;. &lt;code&gt;run&lt;/code&gt; expects a &lt;code&gt;java.lang.Runnable&lt;/code&gt; and has no return type, while &lt;code&gt;execute&lt;/code&gt; expects a &lt;code&gt;TimedOperation.Executable&lt;/code&gt; and returns whatever type the generic call was made with. In a future release, &lt;code&gt;TimedOperation.Executable&lt;/code&gt; will be dropped in favor of &lt;code&gt;java.concurrent.Callable&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new TimedOperation("aTagValue").run(() -&amp;gt; System.out.println("I ran!"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TimedOperation.db().run(() -&amp;gt; System.out.println("Pretend I am a database call!"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List&amp;lt;String&amp;gt; aList = new TimedOperation&amp;lt;List&amp;lt;String&amp;gt;&amp;gt;("aTagValue").execute(() -&amp;gt; Collections.emptyList());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List&amp;lt;String&amp;gt; aList = TimedOperation.&amp;lt;List&amp;lt;String&amp;gt;&amp;gt;http().execute(() -&amp;gt; Collections.emptyList()); //Pretend this is an HTTP request!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A negligible difference (of a few milliseconds) can be expected between the final log, prometheus output, and the health request output. That happens because these calculations happen at the end of the request, but the timer is still ticking. We could stop the timer as soon as the domain logic is over for this wrap-up to use a stopped timer, but this is part of the request after all. Be it as it may, a future release will include a configuration to stop the timer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Although a configuration module exists and is accessible via &lt;code&gt;Chassis.getInstance().getConfig()&lt;/code&gt;, currently no changes to the configurations can be made. Be it as it may, the following configurations are in use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useCallingClassNameForLogging&lt;/code&gt;: Defaults to &lt;code&gt;true&lt;/code&gt;. Governs whether the stack trace will be used or not to initialize the logging class. When creating an &lt;code&gt;ApplicationLogger&lt;/code&gt;, you can provide a class or not to be exported as the logger name. In case where no class is provided, as in &lt;code&gt;Context.forRequest().createLogger()&lt;/code&gt;, this configuration will be used to initialize the class. If it's true, the class will be the calling class. If it's false, it will always be &lt;code&gt;ApplicatonLogger&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;printLoggingContextAsJson&lt;/code&gt;: Defaults to &lt;code&gt;true&lt;/code&gt;. Governs whether the &lt;code&gt;context&lt;/code&gt; field present in log messages will be exported as &lt;code&gt;json&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;allowDefaultLoggingAttributesOverride&lt;/code&gt;: Defaults fo &lt;code&gt;false&lt;/code&gt;. Governs whether automatic logging attributes, such as &lt;code&gt;transactionId&lt;/code&gt;, can have their value replaced with a call for &lt;code&gt;Context#withRequestContextEntry&lt;/code&gt; as in &lt;code&gt;Context.forRequest.withRequestContextEntry("transactionId", aNewValue)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;validatorFailOnExecutionError&lt;/code&gt;: Defaults to &lt;code&gt;true&lt;/code&gt;. Governs whether an unexpected error in a validation attempt results in an exception. Every validation that fails for not matching the annotations for the current operation will result in an exception. For whatever reason, an exception can happen in the middle of the validation attempt. Let's say a method that is being validated throws an exception. If this configuration is true, the validation will not happen, and an exception will be thrown. If this configuration is false, the validation will not happen, but it will be ignored, and if no other validation fails, the &lt;code&gt;Validatable&lt;/code&gt;would be deemed valid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;forbidUnauthorizedContextCreation&lt;/code&gt;: Defatuls to &lt;code&gt;true&lt;/code&gt;. &lt;code&gt;Context.initialize&lt;/code&gt; can't be called anywhere to create a &lt;code&gt;Context&lt;/code&gt;. Allowing the &lt;code&gt;Context&lt;/code&gt; creation is error-prone and, in almost all cases, not needed. If this configuration is true, unless the class in which &lt;code&gt;Context.initialize&lt;/code&gt; is being called is annotated with &lt;code&gt;@ContextCreator&lt;/code&gt;, an exception will be thrown.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;allowContextCorrelationIdUpdate&lt;/code&gt;: Defaults to &lt;code&gt;true&lt;/code&gt;. Governs whether a &lt;code&gt;correlationId&lt;/code&gt; can be updated after the context has been initialized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;monitoringRequestDurationRanges&lt;/code&gt;: Defaults to &lt;code&gt;new double[]{200, 500, 1000, 2000, 5000, 10000}&lt;/code&gt;. Govern the default &lt;code&gt;Histogram&lt;/code&gt; buckets for request duration in milliseconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;exportRequestDurationMetricByType&lt;/code&gt;: Defaults to &lt;code&gt;true&lt;/code&gt;. Governs whether &lt;code&gt;Histogram&lt;/code&gt; metrics for request duration will be tagged with timer types.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;healthTimeWindowDuration&lt;/code&gt;: Defaults to 5 minutes. Governs the maximum age of requests to be used in health calculations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Next Steps
&lt;/h1&gt;

&lt;p&gt;A few future updates have been thought of. Having said that, this does not neither represent an order of priority nor a commitment. These are just a few items that might be done in the future.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable configuration changes.&lt;/li&gt;
&lt;li&gt;Create a configuration to not log stack traces.&lt;/li&gt;
&lt;li&gt;Create a configuration to log attached fields in their own fields.&lt;/li&gt;
&lt;li&gt;Allow for &lt;code&gt;FieldRenderable&lt;/code&gt; to print nested &lt;code&gt;Renderable&lt;/code&gt; objects.&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;@Minimum&lt;/code&gt; ignore value from 0 to &lt;code&gt;Integer.MIN_VALUE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allow for &lt;code&gt;@OneOf&lt;/code&gt; to accept values other than &lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create further validations, such as the maximum value permitted, and the minimum and the maximum size.&lt;/li&gt;
&lt;li&gt;Create a configuration to have some level of control in automatic logs and metrics.
&lt;/li&gt;
&lt;li&gt;Remove the need to call &lt;code&gt;.log()&lt;/code&gt; for messages that have no attachment.
&lt;/li&gt;
&lt;li&gt;Evolve the way this framework interacts with Spring to remove the need for &lt;code&gt;@ComponentScan&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;TimedOperation.Executable&lt;/code&gt; with &lt;code&gt;java.concurrent.Callable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a configuration to calculate application health as a media instead of worst.&lt;/li&gt;
&lt;li&gt;Allow extra tags in automatic metrics.&lt;/li&gt;
&lt;li&gt;Create a configuration to stop the timer as soon as the domain logic is over (&lt;code&gt;Request#doProcess&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Create a summary type metric;&lt;/li&gt;
&lt;li&gt;Create some kind of label structure to override default names for metrics, tags, and logging fields.&lt;/li&gt;
&lt;li&gt;Allow request duration metrics to be collected in measurements different from milliseconds.
&lt;/li&gt;
&lt;li&gt;Create mechanism to work with Java NIO.&lt;/li&gt;
&lt;li&gt;Create a configuration to not swallow rendering transformation errors, which is currently being done.&lt;/li&gt;
&lt;li&gt;Create a configuration to control whether an exception in a request will be wrapped in a &lt;code&gt;RequestException&lt;/code&gt; that holds the original exception, and the outcome of the request.&lt;/li&gt;
&lt;li&gt;Implement distributed tracing.&lt;/li&gt;
&lt;li&gt;Implement default validators.&lt;/li&gt;
&lt;li&gt;Implement yml configuration for things like validations, and more.&lt;/li&gt;
&lt;li&gt;Create a circuit breaker or failsafe structure that can be used to wrap calls.&lt;/li&gt;
&lt;li&gt;Implement structure to enable A/B testing.&lt;/li&gt;
&lt;li&gt;Enable external configuration initialization, be it from yml or from some tool.&lt;/li&gt;
&lt;li&gt;Evolve rendering transformation&lt;/li&gt;
&lt;li&gt;Dynamic validation rule loading from a datasource.&lt;/li&gt;
&lt;li&gt;Cross-field or cross-concern validation.&lt;/li&gt;
&lt;li&gt;Creation of behavior flows using yml or other configuration tools. Possibly another project that uses this one as lib.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>microservices</category>
      <category>springboot</category>
      <category>architecture</category>
    </item>
    <item>
      <title>[en-US] Bad design leads to bad architecture</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Thu, 28 Apr 2022 19:00:05 +0000</pubDate>
      <link>https://dev.to/renatolsjf/en-us-bad-design-leads-to-bad-architecture-3ip7</link>
      <guid>https://dev.to/renatolsjf/en-us-bad-design-leads-to-bad-architecture-3ip7</guid>
      <description>&lt;p&gt;Obviously, the inverse is also true. The lack of a well-thought architecture leads to friction points, especially when speaking of integration. Add this to a big development team and most surely there will be code-level issues.&lt;/p&gt;

&lt;p&gt;Either way, long before we delve into software architecture, we work - and bang our heads a lot - as software developers. If you work in an environment that respects you as an individual, you, as a developer, routinely think about the structure of your code and implement it as you see fit. This is not to say that there will be no coding patterns or practices such as code reviews and the like, but you are responsible for thinking; sometimes you have a good idea, sometimes not so much.&lt;/p&gt;

&lt;p&gt;Developers are not - or shouldn't be - robots. Thinking is an extremely important part of a developer's life or of any professional, really. I believe the most efficient way of professional development is to allow the developer to be creative as it is not easy to come up with a good solution to some kind of problem. I always try to allow the developer, especially those who don't have much experience, to implement their ideas so that we can later discuss the reasons behind that idea and come to a conclusion together as to why it is or is not suitable.&lt;/p&gt;

&lt;p&gt;Be that as it may, I am not here to talk about the process. I just want to reiterate that while we don't even dream of software architecture, we are already responsible for software design. While being a good developer does not mean that one is or will be a good architect, understanding how to structure an application's code well is certainly of great value.&lt;/p&gt;

&lt;p&gt;The problem is that no matter how many years of experience I may have or how many times I work on this kind of activity, thinking about software design is always a hard task. And to those who don't agree: or you are just good at it or our understandings of a good design is quite different.&lt;/p&gt;

&lt;p&gt;If you belong to the first group, lucky you! If you fall into the second, just know divergences are normal and this doesn't make me more right or wrong than you - I'd also like to make it clear that I analyze everything from an object-oriented prism and I don't get into talking about functional programming, for example.&lt;/p&gt;

&lt;p&gt;A few days ago, I saw a small discussion on LinkedIn about the following illustration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioi6xu7kobvgjd9su7qs.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioi6xu7kobvgjd9su7qs.jpeg" alt="Image description" width="627" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All responses, except for one, agreed with the idea presented by the image. According to the self-title of each participant, different levels of seniority were in agreement, from juniors to tech leads. Of course, in practical terms, those who speak up are generally in agreement with the idea and do so as a form of support. Either way, the percentage of people who agree or disagree is irrelevant, the key point is that agreeing with this is not directly linked to experience.&lt;/p&gt;

&lt;p&gt;Well... I find this illustration, to say the least, absurd, and I must say that it's actually quite hard to even reason why. What the illustration says, to me, is: "patterns, OO, and abstraction are bad and unnecessarily complex artifacts, program simply".&lt;/p&gt;

&lt;p&gt;For me, there is a fundamental flaw in this statement. What the illustration calls "super simple code", I call "simplistic code". Always seek simplicity, but never be simplistic. Simple means being able to filter out unnecessary complexity while simplistic means ignoring fundamental factors of the overall goal.&lt;/p&gt;

&lt;p&gt;It doesn't take much to verify that the illustration is flawed and, effectively, simplistic. It ignores all goals of an application, design, or architecture to make a false claim that those with years of experience don't use OO. Admittedly, OO is not the correct paradigm for every situation, but it has absolutely nothing to do with experience.&lt;/p&gt;

&lt;p&gt;Phrases like "I might need this later" or "this is what the experts do" are cheap and contentless disqualifications, as they are based on the idea that abstractions are commonly created on a whim.&lt;/p&gt;

&lt;p&gt;The fact is, changes are common in the software development world and abstractions are perfect tools for isolating complexity. Design Patterns are not even worth getting into: they are proven solutions to common problems, they are not just some trend among less experienced developers.&lt;/p&gt;

&lt;p&gt;I also wonder what the author considers as OO, given the overwhelming majority of Java web applications are not object-oriented at all. The perception I have, however, is that our understandings of what an OO code is are not that far apart.&lt;/p&gt;

&lt;p&gt;Correct use of object orientation allows an application to accommodate gradual change and grow sustainably. Unfortunately, it's common to see cases where a service is created and, within a few (or several) months, problems arise: messy code, excessive complexity in making changes, an unnecessarily long time to make a simple change, etc. The interesting thing is that in most cases I've seen this happen, the applications were "simple".&lt;/p&gt;

&lt;p&gt;Whenever changing or implementing new functionality, we always have to evaluate the application as a whole and what the insertion of new code entails. New functionality is never just that. It is easy to develop a piece of code that is very focused on delivering the behavior, but the truth is that a change has a much deeper impact on the structure of the code. It is at this time that we must evaluate whether our structures are suitable for the software and it may be that, at this moment, it is necessary to create abstractions, change existing structures, etc. This is being simple, it is knowing what level of complexity is necessary to achieve a goal. Being simple is hard, being simplistic is easy.&lt;/p&gt;

&lt;p&gt;Furthermore, we commonly ignore what I call "support code". A domain does not exist in a vacuum, it needs an adequate logic to support it. The support code is not analogous to the concept of "pure fabrication", even more so if you consider that a spring service is the example implementation of "pure fabrication". A fairer comparison, perhaps, is to think of the microservices chassis pattern. Anyway, the structural code doesn't necessarily only support common functionality (logging, tracing, etc).&lt;/p&gt;

&lt;p&gt;The result of these design issues is problems in the architecture. The difficulty of creating OO code is often responsible for creating a huge and unnecessary amount of microservices. Since we cannot separate complexity through abstractions, we physically separate complexity into services in search of loose coupling. To me, it's a misguided effort: it leads to exacerbated coupling, albeit masked and often ignored. It also causes an explosion of smaller and less relevant services, and creates bad services such as "entity traps".&lt;/p&gt;

&lt;p&gt;Some are a fan of nanoservices and I do not disagree that they make sense for some contexts; I understand that they can be very useful components, especially when we think about an architecture with technical partitioning. My point here is that bad design creates an unnecessary service explosion.&lt;/p&gt;

&lt;p&gt;Implementing changes, or rather creating OO code that adequately supports changes, is so difficult that I once heard someone argue that microservices are made to be thrown away. I must admit that, in this case, I can't even reason as to why that would happen. The idea of ​​a disposable service does not enter my mind, a service that is recreated when something goes wrong or when a change is big enough. Not that it doesn't happen, but it's certainly not this mindset that should guide the design of an application. Again, what we see is the creation of services without an adequate level of evaluation and a lack of attention to the whole.&lt;/p&gt;

&lt;p&gt;And what is the result of this architecture driven by bad design? This is where the famous distributed monoliths arise. Parts that are excessively dependent on each other, functionality distributed without a guiding foundation, and difficulty to maintain and operate.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>oop</category>
    </item>
    <item>
      <title>[pt-BR] De design de software ruim a uma arquitetura de software ruim</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Thu, 28 Apr 2022 18:59:59 +0000</pubDate>
      <link>https://dev.to/renatolsjf/pt-br-de-design-de-software-ruim-a-uma-arquitetura-de-software-ruim-1c0a</link>
      <guid>https://dev.to/renatolsjf/pt-br-de-design-de-software-ruim-a-uma-arquitetura-de-software-ruim-1c0a</guid>
      <description>&lt;p&gt;Um design ruim leva a uma arquitetura ruim. Obviamente, o inverso também é verdade, a falta de uma arquitetura bem pensada muitas vezes leva a pontos de atrito, principalmente quando se fala em integração; some isto a um time grande e com certeza surgirão problemas a nível de código.&lt;/p&gt;

&lt;p&gt;De qualquer forma, muito antes de nos aventurarmos com arquitetura de software, atuamos - e batemos muito a cabeça - como desenvolvedores. Se você trabalha em um ambiente que respeita minimamente sua capacidade como indivíduo, você, como desenvolvedor, pensa rotineiramente na estrutura do seu código e implementa da forma que achar melhor. É claro que não estamos falando que não haverá padrões de codificação ou práticas como code reviews e afins, mas você é responsável por pensar; as vezes você tem uma boa ideia e as vezes nem tanto.&lt;/p&gt;

&lt;p&gt;Desenvolvedores não são - ou não deveriam ser - robôs. Pensar é uma parte extremamente importante no desenvolvimento de um desenvolvedor e isso provavelmente se aplica a qualquer profissional, na verdade. Acredito que a maneira mais eficiente de desenvolvimento profissional é permitir que o desenvolvedor seja criativo e bata a cabeça; não é fácil pensar em qual a melhor forma de solucionar um problema. Eu sempre busco permitir que o desenvolvedor, principalmente aqueles que não tem tanta experiência, implemente suas ideias para depois discutirmos os motivos por trás daquela ideia e chegarmos juntos a conclusão do por que ela é ou não adequada.&lt;/p&gt;

&lt;p&gt;Seja como for, o enfoque aqui não é o processo. Apenas reitero que enquanto nem sonhamos com arquitetura de software, já somos responsáveis pelo design do software. Enquanto ser um bom desenvolvedor não significa que a pessoa é ou será um bom arquiteto, entender como estruturar bem o código de uma aplicação certamente é de grande valia. &lt;/p&gt;

&lt;p&gt;É aí que o problema entra; não importa quantos anos de experiência eu tenha ou quantas vezes eu realize este tipo de atividade, pensar no design do código de uma aplicação é, para mim, sempre uma tarefa difícil. E, para quem discorda, existem duas possibilidades: ou você é simplesmente muito bom nisto ou o seu conceito de um bom design é bem diferente do meu.&lt;/p&gt;

&lt;p&gt;Se você está no primeiro grupo, sorte sua! Se você se enquadra no segundo, divergências são normais e isto não me torna mais certo ou mais errado que você - também deixo claro que analiso tudo sobre o prisma de orientação a objetos e não me meto a falar sobre programação funcional, por exemplo.&lt;/p&gt;

&lt;p&gt;Há alguns dias atrás, vi uma pequena discussão no linkedin sobre a seguinte ilustração: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioi6xu7kobvgjd9su7qs.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioi6xu7kobvgjd9su7qs.jpeg" alt="Image description" width="627" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Todos os posts, com exceção de um, concordavam com a ideia apresentada pela imagem. Segundo à auto intitulação de cada participante, diversos níveis de senioridade estavam de acordo, desde júniores até tech leads. É claro que, em efeito prático, geralmente quem se manifesta está de acordo com a ideia e o faz como forma de apoio. De qualquer forma, é irrelevante a porcentagem de pessoas que concorda ou discorda, o ponto chave é que concordar com isso não, não está diretamente ligado à experiência. &lt;/p&gt;

&lt;p&gt;Bom... já eu acho esta ilustração, no mínimo, absurda, e devo dizer que, em certo ponto, é até difícil argumentar do por quê. Traduzindo em miúdos, o que a ilustração diz, pra mim, é: "padrões, oo e abstração são ruins e artefatos desnecessariamente complexos, programe de forma simples".&lt;/p&gt;

&lt;p&gt;Só que, para mim, existe uma falha fundamental nesta afirmação. O que a ilustração chama de "código super simples", eu chamo de "código simplista". Sempre busque a simplicidade, mas nunca seja simplista. Simples significa sermos capazes de filtrar a complexidade desnecessária enquanto simplista significa que ignoramos fatores fundamentais do objetivo geral.&lt;/p&gt;

&lt;p&gt;Não é necessário muito aprofundamento para verificar que a ilustração é demasiadamente falha e, efetivamente, simplista. Ela ignora todo e qualquer objetivo de uma aplicação, design ou arquitetura para realizar uma afirmação genérica de que quem tem anos de experiência, não usa OO. Admitidamente, OO não é o paradigma correto para toda e qualquer situação, mas isto não tem relação absolutamente nenhuma com a experiência.&lt;/p&gt;

&lt;p&gt;Frases como "eu posso precisar disto depois" ou "isto é o que os especialistas fazem" são desqualificações baratas e sem conteúdo, pois são enviesadas na ideia de que abstrações são criadas, comumente, por capricho. &lt;/p&gt;

&lt;p&gt;Fato é que mudanças são comuns no mundo de desenvolvimento de software e as abstrações são ferramentas excelentes para isolar complexidade. Padrões de projeto, então, não vale nem adentrar na discussão: são efetivamente ferramentas que comprovadamente resolvem problemas e não um modismo.&lt;/p&gt;

&lt;p&gt;Também me questiono sobre o que o autor considera como OO, tendo em vista que a esmagadora maioria dos serviços web Java, nada tem de orientação a objetos. A percepção que eu tenho, porém, é de que nossos entendimentos sobre o que é um código OO não estejam tão distantes assim.&lt;/p&gt;

&lt;p&gt;A utilização correta da orientação a objetos permite que uma aplicação acomode mudança gradual e cresça de forma sustentável. Infelizmente, é mais comum do que gostaríamos casos onde um serviço nasce e, dentro de alguns (ou vários) meses, problemas do tipo acontecem: código bagunçado; complexidade de se realizar uma mudança; tempo desnecessariamente grande para fazer se fazer uma mudança, em tese, simples; etc. O interessante é que, na maior parte dos casos que já vivenciei, as aplicações eram "simples". &lt;/p&gt;

&lt;p&gt;Sempre que vamos implementar uma mudança ou funcionalidade, sempre temos que avaliar a aplicação como um todo e o que a inserção do novo código acarreta. Uma inclusão de funcionalidade nunca é apenas isso. É fácil desenvolver um pedaço de código muito focado em entregar o comportamento, mas a verdade é que uma mudança tem impactos muito mais profundos sobre a estrutura do código. É nessa hora que devemos avaliar se nossas estruturas são adequadas para o software e pode ser que, neste momento, seja necessário a criação de abstrações, mudança em estruturas existentes, etc. Isto é ser simples, é saber qual o nível de complexidade necessário para se atingir um objetivo. Ser simples é difícil, ser simplista é fácil.&lt;/p&gt;

&lt;p&gt;Ademais, comumente ignoramos aquilo que chamo de "código de suporte". Uma domínio não existe no vácuo, ele precisa de uma lógica adequada que o sustente. O código de suporte não é análogo ao conceito de "pure fabrication", ainda mais se você considera que um serviço spring é a implementação exemplo de "pure fabrication". Uma comparação mais justa, talvez, seja pensar no padrão chassi de microservices. De qualquer forma, o código estrutural não necessariamente suporta apenas funcionalidades comuns (logging, tracing, etc).&lt;/p&gt;

&lt;p&gt;O resultado destes problemas de design são problemas na arquitetura. A dificuldade de criar um código OO é, muitas vezes, responsável pela criação de uma quantidade enorme e desnecessária de microservices. Uma vez que não conseguimos separar a complexidade através de abstrações, separamos a complexidade fisicamente em serviços na busca do baixo acoplamento. Para mim, é um esforço equivocado: isto tende, na verdade, a exacerbar o acoplamento de forma mascarada; faz com que haja uma explosão de serviços cada vez menores e menos relevantes e cria serviços ruins, como "entity traps". &lt;/p&gt;

&lt;p&gt;Há quem seja fã de nanoservices e não descordo que façam sentido para um determinado contexto; entendo que podem ser componentes muito úteis, principalmente quando pensamos numa arquitetura com particionamento técnico. O meu ponto aqui é que um design ruim cria uma explosão de serviços desnecessária.&lt;/p&gt;

&lt;p&gt;Implementar mudanças, ou melhor, criar um código OO que suporte mudanças de forma adequada é tão difícil que, certa vez, ouvi alguém argumentar que microservices são feitos para serem jogados fora. Devo admitir que, neste caso, eu sequer consigo racionalizar sobre uma possível razão para uma afirmação desta. Não me entra na cabeça a ideia de um serviço descartável, um serviço que é recriado quando algo sai do plano ou quando uma mudança é suficientemente grande. Não que isso não aconteça, mas certamente não é essa mentalidade que norteia o design de uma aplicação. Novamente, o que vemos é a criação de serviços sem um nível adequado de avaliação e o desprezo do todo.&lt;/p&gt;

&lt;p&gt;E qual o resultado dessa arquitetura guiada pelo design ruim? Ai que surgem os famosos monólitos distribuídos. Peças que dependem excessivamente umas das outras, funcionalidade distribuídas sem um fundamento guia e dificuldade de manutenção e operação.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>oop</category>
    </item>
    <item>
      <title>[en-US] Polymorphic Data: an attempt to convince you to adopt a new design pattern</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Tue, 22 Mar 2022 22:48:36 +0000</pubDate>
      <link>https://dev.to/renatolsjf/en-us-polymorphic-data-an-attempt-to-convince-you-to-adopt-a-new-design-pattern-13le</link>
      <guid>https://dev.to/renatolsjf/en-us-polymorphic-data-an-attempt-to-convince-you-to-adopt-a-new-design-pattern-13le</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;should you want to skip directly to the code, here is the &lt;a href="https://github.com/renatols-jf/polymorphic-data"&gt;Github repository&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;… after all, you never know what sticks these days! I previously said that I would try to sell this idea as a new design pattern. Maybe you'll buy it, maybe you'll laugh at me or maybe you'll appreciate my effort as a kind of consolation prize. It doesn't matter, I'm not actually trying to disseminate a new pattern (but then, again, you never know what sticks these days). My idea is to share a line of thought and have a little discussion - which might not work since it's likely very few people - if any - will read this.&lt;/p&gt;

&lt;p&gt;Design Patterns such as &lt;strong&gt;strategy&lt;/strong&gt; and &lt;strong&gt;chain of responsibility&lt;/strong&gt; are great tools to solve problems in an object-oriented fashion, even more so when we adopt behavior composition. Behavior composition, in turn, is a tool that, in my point of view, is essential to build modern applications. It is so because it has deep impacts on both code design and application architecture: it enables an evolutionary architecture that undergoes constant and gradual change and enables applications to adapt to new needs and realities. Be that as it may, behavior composition is beyond the scope of this article and will be addressed in due course.&lt;/p&gt;

&lt;p&gt;It's interesting how strategy and chain of responsibility complement themselves, after all, the execution chain itself could be seen as an execution strategy. In its most simple implementation, strategy is pretty straightforward: a set of different strategies that receive a given set of parameters to work on. &lt;/p&gt;

&lt;p&gt;Deciding which strategy to use may not be that trivial, but that has nothing to do with the pattern itself. I recall a discussion where it was stated that &lt;strong&gt;strategy&lt;/strong&gt; breaks the &lt;strong&gt;open-closed&lt;/strong&gt; principle. The idea behind this statement is that whenever a new strategy is added, the code must be changed to address the new strategy creation, and thus the principle would be broken. &lt;/p&gt;

&lt;p&gt;This statement is clearly wrong and is the result of a literal understanding of the principle. Should this be true, it would be impractical to reach the open-closed principle in any application whatsoever. It's highly unlikely that as software evolves, no existing code is changed since it's highly likely that new constructs will interact with old constructs and, as such, somewhere down the execution chain a change will have to be made in order to call the new code.&lt;/p&gt;

&lt;p&gt;Open-closed is not about changing lines of code, it is about changing application and domain behavior. To add a new log message, a new object call, or a new condition to decide which strategy to use does not violate this principle - changing a buffering algorithm to compact data or changing the strategy decision mechanism from conditionals to reflection is. &lt;/p&gt;

&lt;p&gt;Back to what matters - I'm sorry for the detour - strategy is a very useful pattern abstraction-wise. But, sometimes, its implementation isn't as simple as it may seem. There are cases in which different strategies may need different data and behavior to its execution. One might say that if your strategies need different inputs to their execution, there is a code design problem. I can't deny it might very well be a possibility, but for now, let's agree that this is not necessarily the case and this could be an honest problem.&lt;/p&gt;

&lt;p&gt;The simplest solution might be to use a map, except that it's not a simple solution but rather a simplistic one. - it exposes data and removes behavior, a recipe for an anemic model that doesn't do well on changes. Still, I don't judge those who resort to such solution - let's just hope the winds of change are merciful or, better yet, that there are no winds of change.&lt;/p&gt;

&lt;p&gt;A while back I had this problem and since I was not feeling comfortable using a map, I found myself in a tight spot. I started doing some research about this issue but without much success. I don't recall exactly what the research yielded, but I do recall a specific solution that was based on parameters abstraction. The solution details are out of scope here, but you can read further on &lt;a href="https://github.com/Sebastian-Nielsen/Strategy-Pattern-with-different-parameters/blob/master/paperThatPresentsTheSolution.pdf"&gt;Extending the Strategy Pattern for parametrized Algorithms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I won't discuss the applicability of the solution proposed above, it was certainly the result of research (and a lot of headaches). Suffice to say that it was not adequate to my case, just as any pattern can't be applied to every situation.&lt;/p&gt;

&lt;p&gt;A brief explanation of my problem is in order. I was in a situation where it was needed to connect two distinct contexts (I'm talking about authentication and authorization here but this would be true in any context, really). The contexts were not known until runtime and any given context could be translated to any desired one - except for protocol incompatibilities. You might be asking what exactly do I mean by contexts - in this case are things like OAuth, SAML, Basic Authentication, etc.&lt;/p&gt;

&lt;p&gt;The fruitless research motivated me to think of a possible way to solve this situation. After an ideation process, I arrived at the solution I call &lt;strong&gt;&lt;em&gt;Polymorphic Data&lt;/em&gt;&lt;/strong&gt; - more details about this process can be found in &lt;a href="https://dev.to/renatolsjf/en-us-modeling-an-oo-domain-a-case-study-about-the-creation-of-a-model-for-an-authentication-and-authorization-gateway-3o9j"&gt;Modeling an OO domain: a case study about the creation of a model for an authentication and authorization gateway&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It wasn't that hard to come up with an idea on how to execute the transition itself - chain of responsibility was quite adequate. The issue was in providing data to each step.&lt;/p&gt;

&lt;p&gt;IMHO, object orientation is about abstraction, about encapsulation. Polymorphic Data provides us a way to encapsulate data and behavior that, otherwise, would be available to all interacting parts, even though each part only needs a small set of it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" alt="Polymorphic Data" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main idea behind the &lt;em&gt;"pattern"&lt;/em&gt; is to enable a bigger data universe to be stored in a single repository while being made available on subsets of correlated data. Actually, it's not about making data available, but to make domain information and behavior available. This way, every strategy/step gets the same instance of &lt;strong&gt;PolymorphicData&lt;/strong&gt; and requests its transformation to the adequate domain, having access solely to the needed behaviors and information.&lt;/p&gt;

&lt;p&gt;The end result is that, in a situation where in order to reach a final objective, dynamic and diverse steps must be executed, a way is reached to share a repository of behaviors and data that specializes according to need, promoting encapsulation and high cohesion. In fact, &lt;strong&gt;&lt;em&gt;PolymorphicData&lt;/em&gt;&lt;/strong&gt; can be seen as a design that promotes a layer of anti-corruption.&lt;/p&gt;

&lt;p&gt;Unlike the parameters solution linked above, I won't make an extensive article available. Here's the &lt;a href="https://github.com/renatols-jf/polymorphic-data"&gt;Github repository&lt;/a&gt; having the pattern's proof of concept. I recommend reading the README.MD and also the classes Javadoc.&lt;/p&gt;

&lt;p&gt;That's it! :)&lt;/p&gt;

</description>
      <category>java</category>
      <category>oop</category>
      <category>architecture</category>
      <category>pattern</category>
    </item>
    <item>
      <title>[pt-BR] Polymorphic Data: uma tentativa de te convencer a adotar um novo padrão de projeto</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Tue, 22 Mar 2022 22:48:25 +0000</pubDate>
      <link>https://dev.to/renatolsjf/pt-br-polymorphic-data-uma-tentativa-de-te-convencer-a-adotar-um-novo-padrao-de-projeto-19nd</link>
      <guid>https://dev.to/renatolsjf/pt-br-polymorphic-data-uma-tentativa-de-te-convencer-a-adotar-um-novo-padrao-de-projeto-19nd</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;caso não queira ler o artigo e queria ver diretamente o código, segue o &lt;a href="https://github.com/renatols-jf/polymorphic-data"&gt;repositório no Github&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;… afinal, vai que cola! Eu já havia dito que tentaria vender esta ideia como um padrão de projeto. Talvez você embarque nesta comigo, talvez você ria de mim ou talvez você aprecie meu esforço. Não importa muito, meu intuito aqui não é efetivamente disseminar um novo padrão, jamais teria tanta ambição (mas, novamente, vai que cola). A ideia é compartilhar uma linha de raciocínio e fomentar a troca de ideias; o que pode não funcionar tendo em vista que é provável que poucas pessoas efetivamente irão ler isto!&lt;/p&gt;

&lt;p&gt;Padrões como &lt;em&gt;&lt;strong&gt;strategy&lt;/strong&gt;&lt;/em&gt; e &lt;em&gt;&lt;strong&gt;chain of responsibility&lt;/strong&gt;&lt;/em&gt; são ótimas ferramentas para solucionar problemas em um código orientado a objetos, principalmente quando adotamos uma abordagem de desenvolvimento pautada em composição de comportamento. Composição de comportamento, por sua vez, é uma ferramenta, sob o meu ponto de vista, essencial para construir aplicações modernas. Isto porque ela tem impactos profundos tanto a nível de design quanto de arquitetura: possibilita uma arquitetura evolutiva, que passa por mudanças graduais e permite que aplicações se adequem a novas necessidades e realidades. Seja como for, composição de comportamento está fora do escopo deste artigo e será abordada em momento oportuno.&lt;/p&gt;

&lt;p&gt;É interessante avaliar como são padrões que se complementam, afinal de contas, a própria cadeia de execução pode ser vista como uma estratégia de execução. Em sua implementação mais simples, o &lt;em&gt;&lt;strong&gt;strategy&lt;/strong&gt;&lt;/em&gt; é bem direto: um conjunto de diferentes estratégias que recebem determinados parâmetros para sua execução. &lt;/p&gt;

&lt;p&gt;A decisão da estratégia pode não ser tão trivial assim, o que não tem relação com o padrão em si. Me recordo agora de uma conversa onde foi-se afirmado que o &lt;em&gt;&lt;strong&gt;strategy&lt;/strong&gt;&lt;/em&gt; quebrava o princípio &lt;em&gt;&lt;strong&gt;open closed&lt;/strong&gt;&lt;/em&gt;. A discussão, na verdade, foi iniciada por uma outra pontuação, mas me chamou a atenção esta afirmativa. A idea por trás da mesma é que sempre que uma nova estratégia é adicionada, o código deve ser modificado para prever a criação e utilização desta estratégia, o que automaticamente violaria o princípio. Em uma rápida pesquisa pelo google, é fácil encontrar a definição: &lt;em&gt;"Aberto para extensão significa que, ao receber um novo requerimento, é possível adicionar um novo comportamento. Fechado para modificação significa que, para introduzir um novo comportamento (extensão), não é necessário modificar o código existente."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Uma definição simples, correto? Antes fosse apenas simples; na verdade, ela é simplista. Não vou dar uma de professor Pasquale aqui, mas, na minha humilde opinião, simples é aquilo na medida, resolve a questão sem complexidade desnecessária, enquanto simplista ignora detalhes importantes do contexto. Aliás, questão delicada é chegar na solução simples! Seja como for, chamo esta definição de simplista numa pura análise sintática; sabemos que a intenção do autor pode não ser exatamente esta, mas dado que cada um interpreta de uma forma, uma contextualização melhor se mostra necessária. Exemplo disto é a conclusão da quebra levantada acima.&lt;/p&gt;

&lt;p&gt;Fato é que, seguindo esta definição no &lt;strong&gt;PÉ DA LETRA&lt;/strong&gt;, é inviável de se atingir o &lt;em&gt;&lt;strong&gt;open closed&lt;/strong&gt;&lt;/em&gt; em qualquer aplicação que seja. É improvável que a evolução de um software não leve a alterações em código existente, afinal de contas, é extremamente provável que construtos novos interajam com construtos antigos e, nesse sentido, algum ponto da cadeia de execução terá que ser modificado para interagir com o novo código. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Open closed&lt;/strong&gt;&lt;/em&gt; não é sobre modificar linhas de código, mas sim sobre modificar comportamento pertinente. Adicionar uma mensagem de log ou incluir uma nova chamada a um objeto numa aplicação não é uma quebra do princípio, assim como adicionar uma nova condicional para seleção de uma nova estratégia também não o é. Alterar um algoritmo de buffer para também compactar uma informação ou trocar a seleção das estratégias de condicionais para reflexão é que configura uma quebra do princípio.&lt;/p&gt;

&lt;p&gt;Voltando ao que interessa - sinto em lhe dizer que o discutido acima foi apenas uma ideia desconexa - o &lt;em&gt;&lt;strong&gt;strategy&lt;/strong&gt;&lt;/em&gt; é um padrão muito útil quando falamos em abstração. Porém, ao contrário do cenário pintado acima, nem sempre sua implementação é tão simples assim. Existem casos onde diferentes estratégias podem precisar de diferentes dados para sua execução. Pode-se argumentar que, se suas estratégias precisam de diferentes &lt;em&gt;inputs&lt;/em&gt;, existe um problema de design no seu código. Não vou negar que esta é uma possibilidade e, de fato existem implementações que assim o são por falta de um design melhor, mas acredito que não necessariamente é o caso (ou ao menos assim espero). Por enquanto, vamos combinar que esta necessidade pode realmente surgir em casos reais e que isto não significa um design ruim.&lt;/p&gt;

&lt;p&gt;A solução mais simples é a utilização de um mapa que será passado como parâmetro, certo? Não, claro que não! E aqui estava eu pensando que simples X simplista fosse um ponto apaziguado entre nós. A passagem de mapa é, na maioria dos casos, uma solução simplista; expõe dados e remove comportamento, uma receita ideal para um modelo anêmico que não acomoda mudança. Ainda assim, não julgo a prática que pode salvar muito trabalho e complexidade - resta rezar para que os ventos de mudança sejam poucos e agradáveis.&lt;/p&gt;

&lt;p&gt;Há pouco tempo atrás me deparei com este problema e, dado que não me agradava em nada usar um mapa, me encontrei numa situação complexa. Comecei a pesquisar sobre o problema sem muito sucesso. Não vou dizer que me recordo exatamente do fruto desta pesquisa, mas lembro especificamente de uma proposta de solução baseada em uma espécie de abstração de parâmetros. Os detalhes da solução não vem ao caso aqui, mas você pode ler mais a respeito no seguinte link: &lt;a href="https://github.com/Sebastian-Nielsen/Strategy-Pattern-with-different-parameters/blob/master/paperThatPresentsTheSolution.pdf"&gt;Extending the Strategy Pattern for parametrized Algorithms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Não vou discutir a aplicabilidade da solução disposta na URL acima, certamente ela foi fruto de pesquisa (e muita dor de cabeça). É suficiente dizer que, para minha situação, ela não me pareceu adequada. Além disso, existem uma série de preocupações que fogem às necessidades da estratégia em si. Isso não é um problema, mas é algo que não era relevante para o meu caso.&lt;/p&gt;

&lt;p&gt;Acredito que convém uma breve explicação do meu problema. Estava numa situação em que era necessário conectar dois contextos distintos (no caso eram dados de autenticação e autorização, mas o domínio do problema existe para qualquer contexto). Os contextos não eram específicos, portanto, a tradução pode ocorrer de qualquer contexto para qualquer contexto dentro de um domínio (neste caso, entenda contexto como OAuth, SAML, Basic Authentication, etc). Sendo assim, eu precisava traduzir informações sem saber, de antemão, qual era o contexto fonte e qual era o contexto alvo.&lt;/p&gt;

&lt;p&gt;A pesquisa infrutífera me motivou a pensar numa possível forma de resolver esta situação. Depois de um processo de ideação, cheguei à solução que chamo de &lt;strong&gt;&lt;em&gt;Polymorphic Data&lt;/em&gt;&lt;/strong&gt;; mais detalhes sobre este processo podem ser verificados em &lt;a href="https://dev.to/renatolsjf/pt-br-modelagem-de-um-dominio-oo-um-estudo-de-caso-sobre-a-criacao-de-um-modelo-para-um-gateway-de-autenticacao-e-autorizacao-5ab3"&gt;Modelagem de um domínio OO: um estudo de caso sobre a criação de um modelo para um gateway de autenticação e autorização&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Não foi tão problemático pensar em como executar a transição em si; &lt;em&gt;&lt;strong&gt;chain of responsibility&lt;/strong&gt;&lt;/em&gt; se mostrou bastante adequado. O problema foi justamente em fornecer as informações para cada passo. A este ponto, acredito já estar claro que mapas e modelos anêmicos não se enquadram como uma solução na minha mente.&lt;/p&gt;

&lt;p&gt;Na minha humilde opinião, o que governa a orientação a objetos é a abstração, o encapsulamento. Polymorphic Data é um meio de encapsular dados (e comportamentos) que, de forma contrária, estariam disponíveis para todas as partes que precisam interagir, ainda que cada parte precise apenas de uma pequena fatia do universo de dados.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" alt="Polymorphic Data" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A idea central por trás do &lt;em&gt;"padrão"&lt;/em&gt; é possibilitar que um universo maior de dados esteja presente num mesmo repositório, mas disponível somente em subconjuntos diretamente correlacionados. Aliás, o correto não é disponibilizar dados, mas disponibilizar informações de domínio relevantes. A alteração de uma informação em um contexto não pode impactar o contexto necessário para o próximo passo na cadeia de execução. Dessa forma, todos os passos recebem a mesma instância de um &lt;strong&gt;PolymorphicData&lt;/strong&gt; e requisitam a transformação para o domínio adequado, tendo acesso unicamente aos comportamentos e informações estritamente necessários.&lt;/p&gt;

&lt;p&gt;O resultado final é que, numa situação onde para se atingir um objetivo final, passos dinâmicos e diversos devem ser executados, se alcança uma forma de compartilhar um repositório de comportamentos e dados que se especializa segundo a necessidade, promovendo o encapsulamento e a alta coesão. De fato, &lt;strong&gt;&lt;em&gt;PolymorphicData&lt;/em&gt;&lt;/strong&gt; pode ser visto como um design que promove uma camada de &lt;em&gt;anti-corruption&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ao contrário da solução de parâmetros linkada acima, eu não disponibilizarei um artigo extensivo sobre a solução. Segue um &lt;a href="https://github.com/renatols-jf/polymorphic-data"&gt;repositório no Github&lt;/a&gt; contendo uma espécie de &lt;em&gt;proof of concept&lt;/em&gt; do padrão. Recomendo ler o README.MD e também o Javadoc das classes. &lt;/p&gt;

&lt;p&gt;É isso! :)&lt;/p&gt;

</description>
      <category>java</category>
      <category>oop</category>
      <category>architecture</category>
      <category>patterns</category>
    </item>
    <item>
      <title>[en-US] Modeling an OO domain: a case study about the creation of a model for an authentication and authorization gateway.</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Fri, 11 Feb 2022 13:31:29 +0000</pubDate>
      <link>https://dev.to/renatolsjf/en-us-modeling-an-oo-domain-a-case-study-about-the-creation-of-a-model-for-an-authentication-and-authorization-gateway-3o9j</link>
      <guid>https://dev.to/renatolsjf/en-us-modeling-an-oo-domain-a-case-study-about-the-creation-of-a-model-for-an-authentication-and-authorization-gateway-3o9j</guid>
      <description>&lt;p&gt;Developing is hard, in particular, I believe developing object-oriented code is hard. In the initial stages of developing a new service, it's not uncommon for me to divide my time into two important tasks: to look at my office's ceiling and my computer's screen. Chances are you've heard of the PDCA cycle or OODA loop. Well, OCCS (office's ceiling - computer's screen) is kinda my tool for continuous improvement as well. The proportion of each task, you ask me? I don't think that's relevant, but… ten minutes looking to the ceiling for every 2 minutes looking at the computer's screen. This way the cycle goes on, almost ad infinitum, while the actual development is nowhere to be seen.&lt;/p&gt;

&lt;p&gt;It's also not uncommon for me to rewrite and redesign the same part of a service four or five times before I even have a proof of concept or minimum viable product and I believe it's almost impossible for an OO design to go under development without changes. For me, the initial draft is really important but it's hardly an adequate representation of the actual code when it's first created. And what happens between one rewrite and another? Ceiling, screen…&lt;/p&gt;

&lt;p&gt;For those of you who consider creating object-oriented code a simple task, I both congratulate and envy you. To be honest, I don't think I'll ever come to a point where I'll think alike. I don't wish to convince you otherwise but a better explanation than "it's hard" is needed.&lt;/p&gt;

&lt;p&gt;To understand why I believe OO modeling is hard, we must first understand what object orientation is and what it is not about. Object orientation is not about code organization, at least not in its fundamental sense and, maybe this surprises you, but it's not about polymorphism and inheritance either. Object orientation is about encapsulation, the thing is that encapsulation is not about creating classes full of private attributes with getters and setters.&lt;/p&gt;

&lt;p&gt;First, let's approach the polymorphism and inheritance issue. Are those essential mechanisms for object orientation? Possibly, but they also are nothing more than means by which object orientation is achieved, and solely its use does not configure OO. It’s easier to develop structural code using the mechanisms above than a true OO code. If classes do not create an OO program, neither do polymorphism and inheritance. &lt;/p&gt;

&lt;p&gt;I believe encapsulation is what best defines OO. To encapsulate is to give responsibility to some unity of code (be it a class or any other type of construct) without questioning it about its data or how its behavior is executed. It's about creating layers of behavior and indirection, composing features with a combination of behaviors, and hiding the complexity. In short: OO is about abstraction. There can not be abstraction without encapsulation and abstraction is hardly well represented without inheritance or polymorphism. The difference is that inheritance and polymorphism can produce structural code while there is no middle ground for encapsulation: that which may be called inadequate encapsulation is, in fact, not encapsulated. &lt;/p&gt;

&lt;p&gt;It's likely that I still haven't been able to express myself correctly and that all that was said above may have sounded abstract - no pun intended - after all, explaining what is OO might just be as hard as creating OO code - those might be connected and part of the difficulty in developing is the result of the lacking of understanding. Bear with me and keep calm: maybe I can share my point of view in a clearer way when we get to the case study. But before, I believe it's important to highlight an issue I often see and believe it's an OO anti-pattern: Implicit Domain.&lt;/p&gt;

&lt;p&gt;Analyzing OO code, it's common to see classes that represent concrete concepts of the real world: person, student, order, etc. But what if our application has nothing to do with those things? What if our application receives some data, transforms it, and produces modified data? Some will try to materialize what the data represents in the real world while others will simply not create what is routinely called a model. And what is the application domain after all? Is it what the data represents or doesn't my application have a specific domain?&lt;/p&gt;

&lt;p&gt;Every application or service has a domain and in the above example, it's certainly not what the data represents. A correct design of the domain is crucial, at least for OO code. Why is it so hard to create a domain model for the given example? I believe it's because when we think about the domain we think about data, not about behavior. An adequate design does not mean a single solution, but for starters, we could use a few concepts to create our domain: pipeline, flow, transformation, etc. Yes, those are - or rather, can be - domain objects, but it's important to notice that the domain of an application depends on its goals.&lt;/p&gt;

&lt;p&gt;The above is an example of what I called implicit domain. For lack of data that configures a real-world domain, an application is left without its domain explicitly mapped in code. This is the first step to creating procedural code - the second is the inadequate use of polymorphism and inheritance and the lack of encapsulation. At the end of the day, both concepts are intertwined since the implicit domain comes from our habit of associating domain to data which, in turn, is a symptom of the lack of encapsulation. &lt;/p&gt;

&lt;p&gt;But now, with no further ado, let's explore our case study. Recently I was introduced to a project in which our role was to be a kind of integrator: we should intermediate the communication between content providers, e.g. streaming platforms, and service providers. The scope is extensive but I'd like to focus on a particular feature: part of the content providers integrate themselves through SSO - the user has access to the content signing into the service provider which is responsible for determining the user's eligibility to the content. Each service provider had a proprietary SSO solution and we were to act only as an authentication and authorization gateway where we should intercept and translate requests. &lt;/p&gt;

&lt;p&gt;The idea behind the feature was that any content provider may be integrated into the SSO mechanism of any service provider. Although it's not so simple commercially speaking, technically it should be possible. The initially supported standard would be Oauth 2.0.&lt;/p&gt;

&lt;p&gt;I believe that - and I may be wrong supposing this - it's natural thinking between Web Java developers to create a default MVC structure - something like AuthController, AuthService, AuthRepository, and the likes of it. While it makes sense, we are not talking about the application domain - for that, it's likely that classes such as OAuth and OAuthData be created as the application domain and, with them, a diversity of getters and setters - or not, praised be Lombok! Either way, the picture is the same: a service class with business rules and the model storing information.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, those are valid structures and I use them as well, but I'd rather limit its reach to entry points and not let this type of concept penetrate the domain. I believe an anemic domain is rather questionable, but I'm certainly not here to criticize development approaches. With a little bit of luck, I might be able to share a different point of view and you may agree or not - or even both, who knows?!! In any case, I'll achieve my goal: knowledge sharing to stimulate critical thinking so that you have arguments to support your point of view, whatever it may be.&lt;/p&gt;

&lt;p&gt;With the main goals outlined, the OCCS cycle (ha!) for the application began. My initial instinct was to map concepts like identity and content provider:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo1m4z8v74wye5c72os0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo1m4z8v74wye5c72os0.png" alt="Concept 1" width="417" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The content provider is a simple class with two methods: one for authentication and another for authorization. It should be enough since those are the two actions needed for the user to enjoy the content. Each identity/service provider can have its logic/standard to authenticate/authorize a user and, as such, It seems logical that the content provider implementation should merely call the identity provider implementation.&lt;/p&gt;

&lt;p&gt;The issue is that authentication and eligibility should not be seen as whole units of the process, that is, they can be composed of several different steps - that is the case with OAuth's Authorization Code Flow. So, instead of creating specific implementations for the identity providers, it seemed better to create one more level of abstraction in which each identity provider delegates, once more, the processing to another component.&lt;/p&gt;

&lt;p&gt;From there, each delegate is free to implement their inner workings. The initial design represents a subdivision of the tasks in steps but that's the implementation of the base delegate and each delegate should create the structure that is better for their algorithm.&lt;/p&gt;

&lt;p&gt;With the initial concept on my mind, I decided to start coding - sadly, no code was written that day! From the very beginning, the development was not flowing very well - slow advances with pauses to take a breath. As I said, that's not uncommon when I first start a new service, the thing is that this time I still hadn't written a single business rule with the proposed design. This is usually a sign of domain problems but, sometimes, it's just a lack of inspiration. Well, just my luck - this time, it was the former!&lt;/p&gt;

&lt;p&gt;Some doubts started appearing: What happens when the standards used are not OAuth x OAuth? What if the service provider's SSO is a proprietary solution with the usage of QR Codes in a mobile device previously authenticated? What if the service provider tokens should not be shared directly and instead correlation tokens should be used? What if the correlation tokens had different refresh policies than their original counterparts? All these possibilities led to several conditionals that would make the code overly complex.&lt;/p&gt;

&lt;p&gt;By this time, a good amount of time had passed and I was certain my domain design was not semantically adequate to represent my problem. The design of a domain must be able to solve a problem but can't be based on a very specific feature - object orientation is to understand a problem and create a domain abstract enough to compose functionality that will solve the issue while supporting gradual change. This does not mean that the service won't change, I build services in a way that they often undergo minor adjustments as the requirements become more clear - what is important is to create a structure that enables the service increment and easy adaptation. This is key and was precisely what my domain was lacking. Back to the drawing board and I got a design that looked like the following (simplified for brevity):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ca9hqtdq34z3ite5xe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ca9hqtdq34z3ite5xe.png" alt="Concept 2" width="401" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this moment, I was sure the delegate was unnecessary for any authentication process will need a composition of behaviors, even more so because the exact needed steps on two authentication mechanisms may be completely distinct - the way the content provider authenticates to us and the way we authenticate to the identity provider. In other words, there would be no single step to achieve authentication/authorization from a content provider to an identity provider.&lt;/p&gt;

&lt;p&gt;And how is adequate behavior achieved? The composition of steps is done through an analysis of the authentication context - this context has enough data to provide information on which mechanism is used between the content provider and us and which is used between us and the identity provider. Besides, it also records information about the process state, message exchange configurations, generated tokens, and related data. Through this information, there is a component responsible for creating the sequence of steps necessary for behavior execution, in a chain of responsibility style.&lt;/p&gt;

&lt;p&gt;Back to development, right? Well, not really. One thing still bothered me: the context needed for each step may vary greatly, how to create a common interface? An issue with patterns such as strategy is that the input needs to be the same and in this case, each step would probably need a different set of data. This could be mitigated by not passing parameters to the strategy call and using dependency injection, instead. This isn't always possible and even if it was in this specific case, the core issue still existed: what would be injected? Should I use a simple hashtable as input for method calls and each step read the needed data? The idea didn't sound correct and, for the third and last time, I went back to the drawing board arriving at the following structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" alt="Concept 3 - Polymorphic Data" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For starters, I should say this is the last version up to now. Change is an undeniable reality in software development and the only way to cope with this reality is to design an abstract enough domain focused on goals and functionality by composition. &lt;/p&gt;

&lt;p&gt;The first thing to notice is that the content provider and identity provider were scraped out. Their removal is the result of the perception that these concepts do not matter to how the authentication will take place - while the actual authentication process depends on these real-world entities, their domain model counterparts would be nothing more than data storage classes. And so, my domain comes down to two basic concepts: AuthStep and AuthContext.&lt;/p&gt;

&lt;p&gt;My domain model now only needs to worry about its authentication context and what steps will be needed to reach the goal. There were no more fixed set of actions that should be taken to achieve a goal -  I found it more appropriate to make the actions more granular, now there are specific actions for the authentication flow initiated by the content provider: authorization code generation, access token generation, token refresh, token revocation, etc. The authentication context holds information on what action was requested and can verify if the action can and should be performed.&lt;/p&gt;

&lt;p&gt;You might be wondering about the context - it still needs to be variable and each step still needs a specific set of data, right? Right, an OAuth context between the content provider and us might turn into a SAML - or any other protocol, for that matter - between us and the identity provider. So, as of now, it doesn't look like the input issue has been solved.&lt;/p&gt;

&lt;p&gt;For this issue, I created a structure that I've called &lt;strong&gt;Polymorphic Data&lt;/strong&gt; - maybe I'll try to sell it as a design pattern in a new article; you can never be too sure on what sticks these days. Using a map as a parameter is a delicate issue as it can be handled in ways not intended and it imposes knowledge of data structure to calling code - besides, it is a data storage without any domain behavior. At the same time, the context is being incremented and used on a chain of actions so its internal state can and should change, but the old context can't be invalidated as the data might be needed again for a different step.&lt;/p&gt;

&lt;p&gt;In the end, AuthContext does use a map/hashtable as its internal data structure, only it is encapsulated, without external visibility, just like the data access is - the calling code does not know from where the data is fetched. The key point is that all information is stored in the same structure but hidden from external code - the secret to making this work is in AuthContext's key method AuthContext::as. This method receives as a parameter a class that inherits from AuthContext and returns the desired context implementation. Each implementation works on the hashtable but exposes behavior to work only on the pertinent data for the current context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OAuthContext oauthContext = authContext.as(OAuthContext.class);
if (!oauthContext.isGrantValid()) { throw new InvalidAuthRequestException(“invalid_grant”); }
return oauthContext; //or authContext, it does not matter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, each step executes the context transformation it needs and the transformation does not change the pertinent data to a different context, which can be obtained on a later step. I came up with polymorphic data for situations when we need to integrate several different domains and contexts and when the behavior is too granular given the versatility of the scenario. &lt;/p&gt;

&lt;p&gt;You might be wondering - each step must extract information from the context to execute its behavior and, if this is the case, isn't the data segregated from the behavior. Indeed it is, but I reckon this is not a break of encapsulation. Each context has the most pertinent behavior inside itself - validate the access token, verify the authorization code, etc. So it happens that behavior like obtaining an access token and consuming protected APIs isn't the responsibility of the context.&lt;/p&gt;

&lt;p&gt;The resulting structure is that domain behavior is almost entirely concentrated on AuthStep and AuthContext - not inside service classes.  To these, I'd rather leave code controlling application logic. If I change my framework, my business rules are intact, although the application code will need to be restructured. &lt;/p&gt;

&lt;p&gt;I believe it's so hard to create a semantically rich model because it's not about knowing how to code - or at least not only about it - it's about knowing the business. In addition, object orientation difficulty is in favoring solutions that structure the behavior in a way that the requirement is satisfied and, yet, reaches a set of goals - both technical and otherwise - instead of solutions that, fundamentally, seek only to achieve the most superficial need of the requirement. After all, it's much easier to develop a service that takes care of OAuth processing than to create an internal structure that enables seamless authentication context migration - both would comply with the initial business rules, but it's clear which would accommodate better gradual change.&lt;/p&gt;

&lt;p&gt;Being able to imagine and trace goals behind a domain is essential. From the start, it was clear to me that we would act as a gateway and that we possibly would need to execute different behaviors on different sets of data. Are there other adequate domain models to build this application and provide the current behavior? Sure, but I'd like to think that our goals have been achieved. Future problems can occur, even if care on the design was taken - the domain or the goals could turn out to be incorrect or inadequate. Still, historically, this way of thinking has facilitated my refactorings and application growth.&lt;/p&gt;

&lt;p&gt;For me, it's interesting that something so essential is so hard. In the end, it's hard because there is no set of steps that will guide you to create an adequate domain - you need to understand the problem, deeply analyze it and its context, and… imagination? In a previous project, I needed several months to create an adequate domain for a set of existing services. While this is subject for another time, know this: abstract thinking is hard - most probably because we are not used to it. Abstract thinking has been part of our lives for a rather short time. We tend to think that computers are somewhat intelligent because they excel in areas that seem hard to us - this is due to the exposure time. After all, walking and dancing is a pretty basic task for most people - ok, dancing not so much - but it's rather hard to build a robot with fluid motion. Again, exposure time.&lt;/p&gt;

</description>
      <category>java</category>
      <category>patterns</category>
      <category>design</category>
      <category>oop</category>
    </item>
    <item>
      <title>[pt-BR] Modelagem de um domínio OO: um estudo de caso sobre a criação de um modelo para um gateway de autenticação e autorização</title>
      <dc:creator>Renato Santos</dc:creator>
      <pubDate>Fri, 11 Feb 2022 13:24:36 +0000</pubDate>
      <link>https://dev.to/renatolsjf/pt-br-modelagem-de-um-dominio-oo-um-estudo-de-caso-sobre-a-criacao-de-um-modelo-para-um-gateway-de-autenticacao-e-autorizacao-5ab3</link>
      <guid>https://dev.to/renatolsjf/pt-br-modelagem-de-um-dominio-oo-um-estudo-de-caso-sobre-a-criacao-de-um-modelo-para-um-gateway-de-autenticacao-e-autorizacao-5ab3</guid>
      <description>&lt;p&gt;Desenvolver é difícil, em especial, acredito que desenvolver orientado a objetos é difícil. Nas fases iniciais de um serviço, não é incomum que eu divida meu tempo em duas tarefas importantes: olhar para o teto e olhar para a tela do computador. A proporção? Não que seja relevante, mas… dez minutos olhando para o teto e dois para a tela; assim o ciclo se repete por um bom tempo, quase que ad infinitum. Já o desenvolvimento passa longe.&lt;/p&gt;

&lt;p&gt;Também não é incomum que eu reescreva uma mesma parte do serviço quatro ou cinco vezes antes de sequer ter uma POC ou MVP. De fato, acredito que dificilmente um design OO passa pelo desenvolvimento sem mudanças; para mim, o esboço inicial é muito importante, mas dificilmente é uma representação suficientemente adequada a nível de código no momento de sua criação. E o que acontece entre uma reescrita e outra? Teto, tela…&lt;/p&gt;

&lt;p&gt;Para os que consideram a criação de um serviço OO tarefa simples, eu os felicito e os invejo. Sinceramente, não acho que algum dia chegarei a pensar da mesma forma. Não quero convencê-los do contrário, mas acho que convém uma explicação sobre o meu ponto de vista.&lt;/p&gt;

&lt;p&gt;Para entendermos porque uma modelagem OO é difícil, ou melhor, porque eu assim acredito, primeiro precisamos entender o que é orientação a objetos. O problema é que definir o que é orientação a objetos não é tarefa simples, mais fácil dizer o que não é; isso tudo sob o meu ponto de vista, claro. Orientação a objetos não é sobre a organização do código, pelo menos não em seu sentido fundamental, e, talvez esta te surpreenda, mas também não é sobre polimorfismo e herança. Orientação a objetos é sobre encapsulamento, o problema é que encapsulamento não é sobre criar classes com atributos privados repletas de getters e setters. &lt;/p&gt;

&lt;p&gt;Abordaremos, primeiramente, polimorfismo e herança. São, estes, mecanismos essenciais para a orientação a objetos? Possivelmente, mas são apenas meios pelos quais se atinge a orientação a objetos, seu uso por si só não configura a orientação a objetos. De fato, é mais simples de se chegar a um código estruturado utilizando os mecanismos citados acima do que a um código OO; se classes não criam um programa OO, tampouco polimorfismo e herança. &lt;/p&gt;

&lt;p&gt;Refletindo sobre o assunto, penso que encapsulamento é o que melhor define a OO. Encapsular é sobre dar uma responsabilidade para alguma unidade de código (seja uma classe ou qualquer outro construto) sem a necessidade de questioná-la sobre seus dados ou como seu comportamento é executado. Também é sobre construir camadas de comportamento e indireção, sobre compor funcionalidades com combinações de comportamentos e sobre esconder a complexidade; não gosto do termo diminuir porque, no final das contas, toda a complexidade necessária para alcançar um objeto deve, invariavelmente, estar presente. Para mim, o que melhor resume é: OO é sobre abstração. Abstração não existe sem encapsulamento e dificilmente é bem representada sem herança ou polimorfismo, a diferença é que herança e polimorfismo podem, em seu conceito mais fundamental, ser utilizados em um código não OO, mas não existe um meio termo para encapsulamento: aquilo que pode vir a ser chamado de um encapsulamento inadequado, na verdade, não está encapsulado.&lt;/p&gt;

&lt;p&gt;É provável que eu ainda não tenha conseguido me expressar corretamente e que o dito acima tenha soado um tanto quanto abstrato, mesmo porque tão difícil quanto praticar um código OO é explicar o que é OO; talvez parte da dificuldade do desenvolvimento seja advinda da dificuldade de entendimento, é algo a se pensar. De qualquer forma, tenha calma: talvez eu consiga compartilhar de forma mais clara meu ponto de vista quando, de fato, chegarmos ao estudo de caso. Mas antes, acredito ser importante ressaltar algo que vejo costumeiramente e que acredito ser um anti-padrão OO: Domínio Implícito.&lt;/p&gt;

&lt;p&gt;Ao analisarmos o código de diversos serviços OO, é comum verificarmos classes que representam conceitos concretos do mundo real: automóvel, carro, pessoa, estudante; exemplos não faltam. Mas o que acontece quando a aplicação não trata nada disso? E se a aplicação recebe um conjunto de dados, realiza transformações e passa os dados para frente? Talvez o ímpeto de alguns seja tentar materializar o que os dados representam no mundo real; outros simplesmente não criarão aquilo que é rotineiramente chamado de modelo. E qual o domínio da aplicação no final das contas? É aquilo que os dados representam ou a minha aplicação não tem um domínio específico?&lt;/p&gt;

&lt;p&gt;Toda aplicação tem um domínio e, no exemplo citado acima, ele certamente não é aquilo que os dados representam. A representação do domínio de forma adequada é essencial, pelo menos para os programas OO. Porque é tão difícil representar um domínio no caso citado? No meu ponto de vista é porque quando pensamos em domínio, pensamos em algo concreto. Ou melhor, é porque quando pensamos em domínio pensamos em dados, não em comportamento. Mas qual seria um domínio adequado para o exemplo acima? Obviamente podemos chegar a diversas soluções distintas, mas alguns pontapés para iniciar a discussão podem ser: pipeline, fluxo, transformação, etc. Sim, estes são (leia-se, podem ser) objetos de domínio; mas é importante ressaltar que o domínio de uma aplicação depende dos seus objetivos.&lt;/p&gt;

&lt;p&gt;O dito acima exemplifica aquilo que chamei de domínio implícito. Por falta de informações que categorizem um domínio do mundo real, uma aplicação fica sem seu domínio explicitamente mapeado em código. Este é o primeiro passo para um código estruturado, o segundo é a utilização inadequada de polimorfismo e herança e a falta de encapsulamento. No final das contas, ambos conceitos se confundem, já que o domínio implícito deriva do nosso costume de associar domínio a dados que, por sua vez, é um sintoma da falta de encapsulamento.&lt;/p&gt;

&lt;p&gt;Mas agora, sem mais delongas, vamos ao nosso estudo de caso. Recentemente, fui inserido em um projeto onde nosso papel é ser uma espécie de integrador: vamos intermediar a comunicação entre provedores de conteúdo, como, por exemplo, plataformas de streaming, e provedores de serviço. O escopo é, de certa forma, extenso, mas gostaria de focar num aspecto menor: parte dos provedores de conteúdo se integra através de SSO (single sign on). Isto quer dizer que um usuário desta plataforma, ao entrar na mesma, seleciona o provedor de serviços pelo qual deseja se autenticar e é o provedor de serviços o responsável por dizer se o usuário em questão é elegível para consumir o conteúdo, isto é, se ele tem direito ou não.&lt;/p&gt;

&lt;p&gt;É esperado que os provedores de serviços (ou, ao menos, a maior parte) já possuam um mecanismo de single sign on próprio; nesse sentido, seremos apenas um gateway de autenticação e autorização, onde vamos intermediar, repassar e traduzir requisições; lembrando que estamos falando apenas da parte do escopo citada acima. A ideia é que qualquer provedor de conteúdo possa se conectar ao SSO de qualquer provedor de serviço; do ponto de vista comercial, certamente não é tão simples assim, mas o objetivo é que, tecnicamente, isto seja possível. O mecanismo de autorização inicialmente suportado seria OAuth 2.0.&lt;/p&gt;

&lt;p&gt;Acredito que, e eu posso estar errado nessa suposição, é um pensamento natural entre os desenvolvedores Java que utilizam frameworks web traçar uma estrutura padrão MVC. Algo como AuthController, AuthService, AuthRepository e afins. Faz sentido, mas até então não estamos falando de domínio em si. É provável que para suprir essa demanda classes como OAuth, OAuthData e/ou afins surjam como o chamado modelo e junto com elas, uma diversidade de getters e setters; ou não, viva ao Lombok!  Seja como for, o esquema é parecido: service com a lógica em si e o model trafegando as informações. &lt;/p&gt;

&lt;p&gt;Não me entenda errado, eu acho que essas estruturas são válidas, eu também as utilizo, mas prefiro tentar limitar seu alcance para pontos de entrada e não deixar que esse tipo de conceito penetre dentro do domínio.  Na minha opinião, o mais questionável é o modelo anêmico, mas eu certamente não estou aqui para criticar abordagens de desenvolvimento. Por sorte, estou aqui para compartilhar um ponto de vista um pouco diferente e você pode concordar ou discordar, ou até ambos, quem sabe?!! Seja como for, o objetivo será atingido: difusão de conhecimento para estimular o pensamento crítico de forma que você tenha argumentos para sustentar seu ponto de vista, independente de qual seja ele.&lt;/p&gt;

&lt;p&gt;Com os objetivos gerais traçados, o ciclo TT (apenas um nome bonito para teto, tela) para essa aplicação se iniciou. O meu instinto inicial foi mapear um domínio com conceitos como provedor de conteúdo e provedor de identidade (content provider e identity provider):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo1m4z8v74wye5c72os0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo1m4z8v74wye5c72os0.png" alt="Conceito 1" width="417" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O provedor de conteúdo é uma classe simples, com dois métodos: um para autenticação e outro para autorização / elegibilidade. Em tese, seria o suficiente, já que são as duas ações necessárias para que os usuários desfrutem do conteúdo. O fato, porém, é que cada provedor de identidade pode ter sua lógica específica para realização dessas ações. Portanto, nada mais lógico que a implementação do provedor de conteúdo apenas chamar a implementação do provedor de identidade.&lt;/p&gt;

&lt;p&gt;A questão é que autenticação e elegibilidade não podem ser encaradas como unidades do processo, isto é, podem ser compostos de diversos passos distintos; é este o caso do Authorization Code Flow, por exemplo. Sendo assim, no lugar de criar implementações específicas para os provedores de identidade, achei mais adequado criar um nível a mais de abstração, onde cada provedor de identidade delega, mais uma vez, a lógica para um outro componente. &lt;/p&gt;

&lt;p&gt;A partir daí, cada delegate é livre para implementar seu funcionamento. A imagem demonstra a subdivisão das tarefas em passos nomeados, não de forma surpreendente, como steps. Mas essa é uma lógica específica de um delegate específico; cada delegate deve criar a estrutura que melhor se adequa ao seu algoritmo. A implementação base que faremos é feita de pequenos passos / estratégias.&lt;/p&gt;

&lt;p&gt;Com o modelo inicial em mente, parti para o início do desenvolvimento; o problema é que o desenvolvimento não partiu junto comigo! Logo de início, o desenvolvimento não fluiu bem; avanços lentos com pausas para oxigenar a mente. Como dito, isto não é incomum quando eu inicio um novo serviço, o problema é que, desta vez, sequer consegui escrever uma regra de negócio na estrutura inicialmente proposta. Isso geralmente é um bom indicativo de problemas de domínio, mas, por vezes, é apenas falta de inspiração. Para a minha sorte, neste caso, o problema se enquadrou na primeira categoria. Questões começaram a surgir: o que acontece se o mecanismo não for OAuth x OAuth? E se o SSO do provedor de serviços for através da leitura de um QR code pela aplicação, proprietária, em um dispositivo móvel já autenticado? E se for necessário que os tokens não sejam repassados diretamente, sendo criados tokens correlatos para repasse aos provedores de conteúdo? E se os tokens correlatos tiverem um mecanismo de atualização distinto dos tokens gerados diretamente para o integrador e a validade do token correlato seja válida enquanto o token original tenha expirado? Todos esses questionamentos podem levar a uma série de condicionais problemáticas que tornariam o código complexo.&lt;/p&gt;

&lt;p&gt;Neste ponto, um bom tempo já havia se passado e eu estava decidido que meu domínio não era semanticamente adequado para representar o meu problema. A representação de um domínio precisa ser capaz de resolver um problema, mas não pode ser amarrado em uma funcionalidade extremamente específica; a orientação a objetos é justamente entender o problema e criar um domínio suficientemente abstrato para compor a funcionalidade que resolverá a questão suportando a mudança gradual. Obviamente isto não quer dizer que o serviço não sofrerá alterações, inclusive, eu construo meus serviços de modo que eles geralmente sofrem pequenas adequações à medida que os requisitos ficam mais claros. A questão é que procuro fornecer ao serviço uma estrutura que permita seu fácil incremento e adaptação. Este é o ponto chave e parecia ser justamente aquilo que meu modelo não tinha; de volta ao desenho, saí com uma estrutura parecida com a seguinte (representação simplificada por mérito de brevidade):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ca9hqtdq34z3ite5xe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ca9hqtdq34z3ite5xe.png" alt="Conceito 2" width="401" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neste momento, estava decidido que o delegate era um passo desnecessário, pois, na minha visão qualquer processo de autenticação vai, necessariamente, precisar passar por uma composição de comportamento, mesmo porque o que exatamente acontecerá depende de dois mecanismo de autenticação que podem ser completamente distintos: como é feita a autenticação do provedor de conteúdo com o integrador e como é feita a autenticação do integrador com o provedor de identidade. &lt;/p&gt;

&lt;p&gt;E como o comportamento adequado é composto? A composição é feita através de uma análise do contexto de autenticação; esse contexto é rico o suficiente para fornecer informações sobre qual o mecanismo utilizado entre provedor de conteúdo e o integrador e qual o utilizado entre integrador e provedor de identidade. Além disso, também registra informações sobre o estado atual do processo, configurações de trocas de mensagens, tokens gerados e afins. Mediante estas informações, existe um componente responsável por criar a sequência de passos necessária para sua execução, num estilo chain of responsability.&lt;/p&gt;

&lt;p&gt;Agora, de volta ao desenvolvimento, certo? Errado! Uma coisa me incomodava: o contexto necessário para cada passo pode variar de forma extrema, como representar a chamada? Um problema de padrões como o strategy, por exemplo, é que, necessariamente, o input de dados precisa ser o mesmo e, neste caso, cada passo teria um input bem diverso. Isso pode ser mitigado simplesmente não passando quaisquer valores como parâmetros para a chamada da estratégia em si, mas sim injetando a dependência na criação do passo. Isso nem sempre é possível e ainda que fosse, neste caso, o problema permanecia: o que seria injetado? Eu deveria utilizar um simples mapa como input das chamadas e cada passo recuperaria as informações necessárias? A ideia não me agradava muito e, pela terceira e última vez no quadro de desenhos, pensei na seguinte estrutura:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjgdddt07sriv6u57odj.png" alt="Conceito 3 - Polymorphic Data" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Em primeiro lugar, é necessário dizer que esta é a última versão até o momento. Como ponto positivo, porém, ressalto que o MVP está basicamente concluído e isto quer dizer que, a menos que o objetivo geral da aplicação mude, dificilmente o domínio passará por mudanças gigantes; incremento e adaptação, sempre existirão. Outra hipótese para mudanças significativas seria um terceiro design inadequado, claro; no momento, prefiro me confortar no pensamento de que este não é o caso.&lt;/p&gt;

&lt;p&gt;O primeiro ponto é o sumiço do provedor de conteúdo e do provedor de identidade. Essa remoção foi fruto da percepção de que estes conceitos pouco importam sobre como a  autenticação acontecerá; por mais que, logicamente faz toda diferença, no final das contas se resume a quais protocolos serão utilizados com quais configurações. A representação dos provedores é desnecessária porque são apenas armazéns de dados. Sendo assim, meu domínio se resume a dois conceitos básicos: AuthStep e AuthContext.&lt;/p&gt;

&lt;p&gt;Tenho uma aplicação que precisa se preocupar apenas com o seu contexto de autenticação e quais os passos serão necessários para atingir o objetivo. E quais objetivos são esses? Com o sumiço do provedor de conteúdo e de identidade, os métodos de autenticação e elegibilidade não faziam mais sentido; cabe ressaltar, também, que eles nunca foram ações satisfatórias para este contexto. Achei mais adequado tornar as ações mais granulares, agora existem ações específicas para o fluxo de autenticação iniciado pelo provedor de conteúdo (que não faz mais parte do domínio): geração do authorization code, geração do access token, refresh dos tokens, revoke e por aí vai. O meu contexto tem o registro de qual ação foi requisitada e é capaz de validar se a mesma pode ser realizada.&lt;/p&gt;

&lt;p&gt;Mas e o contexto? Ele é variável e cada passo ainda vai precisar de seus dados específicos, certo? Certo, um contexto OAuth entre o provedor de conteúdo e o integrador pode ser transformar num contexto SAML, ou algum procotolo custom, entre o integrador e o provedor de identidade. A forma continua a mesma, cada passo trabalha em cima de um AuthContext e passa este mesmo AuthContext para frente para ser trabalhado pelo próximo passo. Então, é razoável que, a este momento, você se questione sobre como essa situação teve seu aspecto geral melhorado.&lt;/p&gt;

&lt;p&gt;Para a situação descrita acima, eu criei uma estrutura que chamei de &lt;strong&gt;Polymorphic Data&lt;/strong&gt; (ou dados polimórficos, só que bonitinho); talvez eu tente a vender como um possível padrão de projeto em um novo artigo, afinal, vai que cola. A passagem de um mapa é uma questão delicada, é uma estrutura de dados base que pode ser modificada como bem entender, seriam necessários representar os campos com constantes e os problemas não param por aí. Ao mesmo tempo, o contexto que está sendo executado na cadeia não pode mudar; quer dizer, ele pode e deve mudar, mas o contexto antigo não pode ser invalidado e assim que ele assumir novamente o formato antigo, os dados que já foram trabalhados anteriormente devem estar preservados.&lt;/p&gt;

&lt;p&gt;No final das contas, o AuthContext utiliza sim um mapa como estrutura de dados, mas isso é uma estrutura encapsulada, sem visibilidade externa, assim como o seu acesso aos dados é encapsulado, sem precisar dizer ao cliente que o acesso é feito através de uma SQL ou chamada Rest. O ponto chave é que todas as informações se encontram numa mesma estrutura, sem visibilidade externa. O segredo da estrutura está em um um método chave do AuthContext, chamado as. Este método recebe, como parâmetro, uma classe que herda de AuthContext, ? extends AuthContext, e retorna uma implementação concreta do contexto desejado. Esta implementação concreta trabalha sobre o mesmo mapa utilizado na anterior, mas possui o conjunto de comportamentos necessários para atuar e expor somente os dados pertinentes para aquele contexto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OAuthContext oauthContext = authContext.as(OAuthContext.class);
if (!oauthContext.isGrantValid()) { throw new InvalidAuthRequestException(“invalid_grant”); }
return oauthContext; //ou authContext, é irrelevante
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dessa forma, cada passo realiza a transformação para trabalhar com o contexto que precisa e a transformação em si não altera os dados de outro contexto, que pode ser novamente obtido em passo posterior.&lt;/p&gt;

&lt;p&gt;Pensei nos dados polimórficos para situações quando precisamos integrar diversos domínios e contextos diferentes e quando as ações executadas neste domínio são muito granulares, de forma que a maioria não se enquadre na declaração do contexto pela alta versatilidade e variabilidade.&lt;/p&gt;

&lt;p&gt;Uma questão que talvez você esteja pensado é: cada passo deve precisar extrair informações do contexto para realizar sua lógica e, portanto, isto não seria separar dados de comportamento? Sim e sim, mas neste caso, eu não considero uma quebra de encapsulamento. Cada contexto tem o comportamento mais pertinente embutido em si: verificação da validade do access token, validação da utilização do authorization code e por aí vai. Porém, a obtenção de um access token não está intimamente ligada ao access token em si, bem como o consumo de APIs protegidas. Nesse sentido, é necessário que o contexto exponha informações pois existe um conjunto de dados onde as operações realizadas por eles são tão granulares que não me parece adequado juntar as coisas, bem como a sequência de operações pode necessitar de uma parte da informação completamente diversa para cada passo.&lt;/p&gt;

&lt;p&gt;O resultado final é que a lógica da domínio está, praticamente em sua totalidade, concentrada no AuthStep e AuthContext e não em services e afins. A estes, prefiro deixar a lógica estrutural da aplicação. Se eu troco meu framework, minha lógica de negócio está intacta, apesar de, logicamente, eu precisar reestruturar as necessidades da aplicação em si.&lt;/p&gt;

&lt;p&gt;Na minha opinião, é tão difícil criar um modelo semanticamente rico porque não é sobre saber desenvolver, ou pelo menos não apenas sobre isso; é sobre conhecer o negócio. Além disso, a dificuldade de OO está em favorecer soluções que buscam estruturar comportamentos que acomodam o requisito e, ao mesmo tempo, atingem determinados objetivos (técnicos e não técnicos), em detrimento de soluções que se preocupam, fundamentalmente, com a necessidade mais clara representada pelo aspecto direto do requisito.&lt;/p&gt;

&lt;p&gt;Conseguir imaginar e traçar os objetivos por trás de um domínio é essencial. Para mim, neste caso, desde o início estava claro que seríamos uma ponte e que possivelmente precisaríamos executar diferentes processamentos sobre diferentes conjuntos de dados. Existem outros modelos possíveis para representar esta aplicação e para disponibilizar a funcionalidade no pé da letra? Sim, existem, mas acredito que o modelo cumpre com os objetivos mais gerais. É claro que nada disso descarta a possibilidade de problemas posteriores, tanto o domínio quanto os objetivos podem ter sido mapeados de forma inadequada; ainda assim este tipo de composição tem, historicamente, facilitado minhas refatorações.&lt;/p&gt;

&lt;p&gt;E, pra mim, é interessante que algo tão essencial seja tão difícil. No final das contas, é difícil porque não existe um conjunto de passos que te ajudarão a criar um domínio adequado, é necessário entender o problema, avaliar toda a questão num grau de profundidade maior do aparente e… imaginação? Em um projeto anterior, fiquei diversos meses para criar um modelo de domínio adequado aos serviços já existentes. Mas isso é história para outro momento.&lt;/p&gt;

</description>
      <category>java</category>
      <category>patterns</category>
      <category>design</category>
      <category>oop</category>
    </item>
  </channel>
</rss>
