main 530d74cb21eb cached
19 files
270.5 KB
61.2k tokens
1 requests
Download .txt
Showing preview only (287K chars total). Download the full file or copy to clipboard to get everything.
Repository: otaviojava/manual-arquiteto-moderno
Branch: main
Commit: 530d74cb21eb
Files: 19
Total size: 270.5 KB

Directory structure:
gitextract_6f6qi3n6/

├── .gitignore
├── LICENSE
├── README.md
└── manuscript/
    ├── Book.txt
    ├── Subset.txt
    ├── about_me.md
    ├── apendix-a.md
    ├── bibliography.md
    ├── chapter_01.md
    ├── chapter_02.md
    ├── chapter_03.md
    ├── chapter_04.md
    ├── chapter_05.md
    ├── chapter_06.md
    ├── chapter_07.md
    ├── chapter_08.md
    ├── chapter_09.md
    ├── chapter_10.md
    └── resources/
        └── readme.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.vscode

================================================
FILE: LICENSE
================================================
Attribution-NonCommercial 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
	wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More_considerations
     for the public: 
	wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution-NonCommercial 4.0 International Public
License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-NonCommercial 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.
  d. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  e. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  f. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  g. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  h. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  i. NonCommercial means not primarily intended for or directed towards
     commercial advantage or monetary compensation. For purposes of
     this Public License, the exchange of the Licensed Material for
     other material subject to Copyright and Similar Rights by digital
     file-sharing or similar means is NonCommercial provided there is
     no payment of monetary compensation in connection with the
     exchange.

  j. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  k. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  l. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part, for NonCommercial purposes only; and

            b. produce, reproduce, and Share Adapted Material for
               NonCommercial purposes only.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties, including when
          the Licensed Material is used other than for NonCommercial
          purposes.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

       4. If You Share Adapted Material You produce, the Adapter's
          License You apply must not prevent recipients of the Adapted
          Material from complying with this Public License.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database for NonCommercial purposes
     only;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material; and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.

=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.


================================================
FILE: README.md
================================================
# Manual do Arquiteto Moderno

## Objetivo

Neste livro você irá aprender sobre conceitos que fazem parte da vida de profissionais seniores na carreira de desenvolvimento e arquitetura de software. Este livro não é um sumário ou a reescrita de livros reconhecidos no mercado. Portanto, é essencial o entendimento prévio de alguns conceitos abordados no decorrer dos capítulos. O diferencial deste conteúdo é que toda a informação é proveniente da aplicação prática das tecnologias, conceitos e culturas apresentadas como Java Efetivo, Clean Code, Domain-Driven Design, Clean Architecture e Building Microservices. 

Alinhando teoria e prática, este livro traz visões arquiteturais e motivações que costumam levar a determinadas escolhas, além de apresentar seus respectivos trade-offs. 

## O que você encontra no livro

Este livro apresenta tópicos atuais e relevantes que embasarão você na tomada de decisão diária no papel em arquitetura de software.Veja a lista de tópicos discutidos no decorrer deste livro:

    1. A importância de aprender os conceitos ao invés de novos frameworks;
    2. Domain-Driven Design (DDD), where to go next?
    3. Clean Code;
    4. Clean Architecture;
    5. Refatoração;
    6. Banco de dados;
    7. Microservices;
    8. Cloud;
    9. Precisamos falar sobre atualizações;
    10. Destrinchando performance de aplicações;

## O que você não vai encontrar neste livro

* Resumo do contéudo de livros mencionados, listados na bibliografia;
* Explicação de conceitos considerados básicos, como "O que é SOLID", "Como se conectar a um banco de dados". 
* Tutoriais e guias passo a passo. 


## Contribuições

Este livro surgiu da iniciativa de Otávio Santana e se materializou através da colaboração de profissionais seniores e com amplo reconhecimento na comunidade. Cada autor e autora que escreveu o livro possui uma opinião bem embasada e experiência de campo nas tecnologias e práticas descritas. 

O grupo de autores iniciais é composto por Otávio Santana, Karina Varela e Sérgio Lopes, estendendo-se a grandes nomes como Elder Moraes, Maurício Salatino (Salaboy), Sandro Giacomozzi, Francisco (Professor) Isidro, Leandro Domingues.

Com a popularização da iniciativa, a comunidade passa a ativamente colaborar com a revisão do livro. Nosso profundo agradecimento a todos que colaboraram: https://github.com/otaviojava/manual-arquiteto-moderno/graphs/contributors



<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/">Creative Commons Attribution-NonCommercial 4.0 International License</a>.


================================================
FILE: manuscript/Book.txt
================================================
about_me.md
chapter_01.md
chapter_02.md
chapter_03.md
chapter_04.md
chapter_05.md
chapter_06.md
chapter_07.md
chapter_08.md
chapter_09.md
chapter_10.md
apendix-a.md
bibliography.md


================================================
FILE: manuscript/Subset.txt
================================================
about_me.md

================================================
FILE: manuscript/about_me.md
================================================
{width=60%}
![](images/autores__otavio.png)

* **Nome**: Otavio Santana

* **Cargo**: Staff Engineer

* **Bio:** Capacitando pessoas desenvolvedoras em todo o mundo a entregar softwares melhores, com mais rapidez e escalabilidade na nuvem. Otavio é um engenheiro de software apaixonado por foco em nuvem e tecnologia Java. Ele tem experiência principalmente em aplicações de persistência poliglotas e de alto desempenho em finanças, mídia social e e-commerce. Otavio é membro de Grupos de Especialistas e Líder Especialista em vários comitês executivos JCP e JSRs. Ele está trabalhando em vários projetos da Apache e Eclipse Foundation, como Apache Tamaya, MicroProfile, Jakarta EE, onde está liderando a primeira especificação em Jakarta EE com Jakarta NoSQL. Líder do JUG e palestrante global nas conferências JavaOne e Devoxx, Otavio recebeu reconhecimento por suas contribuições para OSS, como o JCP Outstanding Award, membro do ano e JSR inovador, Duke’s Choice Award e Java Champion Award, para citar alguns.

  {pagebreak}
  
  {width=60%}
  ![](images/autores__sergio_elder.png)
  
* **Nome**: Sérgio Lopes

* **Cargo**: Especialista em TI no Banco Itaú S/A

* **Bio:** Desenvolvedor C++, desenvolvedor Java, engenheiro de software, especialista em TI em back-end. Trabalhando em um dos mais robustos Internet Bankings da América Latina. Atuando na criação do framework de desenvolvimento chamado *Universal*, que é o sistema central do Internet Banking Itaú desde 2001 e até os dias de hoje ainda em operação, trabalhando no desenvolvimento de ferramentas de suporte para esse framework, suas especificações e sua manutenção, em C/C++.
  Desenvolvimento do Framework Java, que veio substituir o framework *Universal*, atuando nele desde 2014. Criação de mecanismos de coexistência entre o legado e este novo Framework, responsável por atender às necessidades transversais dos canais digitais, fornecendo ferramentas e serviços para acelerar o trabalho de outros engenheiros.
  Trabalhando com tecnologias como: JAVA, Spring Framework, Spring Boot, Docker, Windows/Linux, SQL, JavaScript, Hibernate, Application Server: JBoss EAP.6.X, Testes automatizados com (JUnit4, DBUnit, JMock, Mockito). Eclipse IDE, Maven, Git, Jenkins. Nexus, C++, Visual Studio, Artifactory, Sonar.
  
  {pagebreak}
  
  {width=60%}
  ![](images/autores__karina.png)
  
* **Nome:** Karina Varela
* **Cargo:** Gerente Técnico Principal de Marketing de Produtos, Red Hat
* **Bio:** Karina M. Varela tem experiência de mais de dez anos em TI, trabalhando em funções como desenvolvedora de aplicações, arquiteta de software, consultora, líder de tecnologia e gerenciamento de marketing de produto. Com uma sólida formação em desenvolvimento de software, ela tem experiência profissional em planejamento, arquitetura, entrega e solução de problemas de software crítico em ambientes empresariais de diferentes setores ao redor do mundo.
A partir de 2020, Karina está trabalhando com a Unidade de Negócios de Serviços de Aplicativos da Red Hat como Gerente de Marketing Técnico, especialista no assunto de Automação de Negócios. Ela é membro da comunidade SouJava, tem paixão por ajudar as comunidades e gosta especialmente de projetos e iniciativas de código aberto. Outro hobby é falar em conferências e já palestrou em conferências como Campus Party, TDC e Women Who Code.

{pagebreak}

{width=60%}
![](images/autores__salaboy.png)

* **Nome:** Mauricio Salatino (Salaboy)
* **Cargo:** Engenheiro de Software
* **Bio:** Mauricio é engenheiro de software na Camunda (<http://www.camunda.com>) e instrutor na LearnK8s (<http://learnk8s.io>). Trabalha com o Kubernetes há cinco anos, treinando equipes e desenvolvendo aplicações nativas da nuvem. Em sua jornada, participou de vários projetos de código aberto, incluindo Zeebe, Jhipster, Spring Cloud e Jenkins X. Anteriormente, Mauricio trabalhou na Red Hat/JBoss, no departamento de Engenharia de Automação de Negócios. Apresentou-se na Kubecon 2019 San Diego e na Kubecon 2020 Amsterdam. Está atualmente envolvido com a CD Foundation (<http://cd.foundation>) e com o projeto Jenkins X (como membro do comitê de direção de bootstrap). 

{pagebreak}

{width=60%}
![](images/autores__elder-moraes.png)

* **Nome:** Elder Moraes
* **Cargo:** Developer Advocate, Red Hat
* **Bio:** Elder ajuda pessoas desenvolvedoras Java a trabalhar em grandes projetos, orientando-os(as) sobre como construir e entregar aplicações seguras, disponíveis e rápidas do lado do servidor. Ele é o autor do Jakarta EE Cookbook e membro do conselho do SouJava, um dos maiores JUGs do mundo. Como defensor do desenvolvedor, ele compartilha experiências e melhores práticas por meio de conteúdo online e em eventos internacionais como JavaOne, The Developers Conference, QCon, Oracle Code One, Campus Party e Devnexus.

{pagebreak}

{width=60%}
![](images/autores__sandro.png)

* **Nome:** Sandro Giacomozzi
* **Cargo:** Engenheiro de Software, TOTVS
* **Bio:** Sandro ajuda pessoas desenvolvedoras Java que trabalham em aplicações corporativas a se tornarem especialistas em Java e DevOps, praticando as habilidades certas. Voluntário e palestrante. Seu objetivo na indústria de software é tornar as organizações e as pessoas mais ágeis por meio de processos e ferramentas. Entregas mais rápidas, eficientes e de qualidade. Pessoas e tecnologia alinhadas à satisfação e entrega ao cliente.

{pagebreak}

{width=60%}
![](images/autores__isidro.png)

* **Nome:** Francisco Isidro

* **Cargo:** Professor, Pesquisador da Universidade Federal do ABC
* **Bio:** Professor Isidro é professor de Ciência da Computação e pesquisador com foco em Ensino de Fundamentos de Programação, Desenvolvimento de *Game Engines* e Computação em Nuvem. Isidro mantém um canal no Youtube que oferece conteúdo gratuito sobre Estruturas de Dados, Sistemas Operacionais, Desenvolvimento Web, Jogos e outros assuntos para todos os profissionais e estudantes que desejam aprimorar seus conhecimentos e entender os fundamentos da Ciência da Computação e Desenvolvimento de Software. Palestrante na Campus Party, The Developer's Conference, QCon e outras conferências, Isidro está sempre ajudando as comunidades de desenvolvedores oferecendo conteúdo técnico e orientação profissional. 

{width=60%}
![](images/autores__leandro.png)

* **Name:** Leandro Domingues
* **Job Title:** Fundador da Cluster Consultoria, MongoDB Champion / Consulting Engineer, Microsoft Data Platform MVP, Community Manager, Speaker
* **Bio:** MongoDB Champion, Microsoft Data Platform MVP, Top 50 Neo4j Certified com grande experiência em manutenção de grandes volumes de dados e aplicações críticas, após vários anos trabalhando com bancos de dados relacionais, voltei minha carreira para bancos NoSQL nos últimos 7 anos. Com toda a experiência acumulada em bancos de dados relacionais, hoje atuo ajudando pessoas e empresas a entrarem ou se manterem no universo de bancos NoSQL, participando desde a idealização de projetos até a manutenção de ambientes replicados e escaláveis, passando pela modelagem de dados até aumento de performance. Como desenvolvedor minha carreira foi trilhada com base em .NET, porém nos últimos anos me dedico a tecnologias OpenSource, me especializei em NodeJS e Python, manutenção de ambientes Linux, etc. Participo constantemente de eventos nacionais e internacionais em busca de atualização e conhecimento principalmente na área de OpenSource. Ministro treinamentos in-company e turmas abertas de MongoDB e Neo4j.
* **Bio:** Professor Isidro é professor de Ciência da Computação e pesquisador com foco em Ensino de Fundamentos de Programação, Desenvolvimento de *Game Engines* e Computação em Nuvem. Isidro mantém um canal no Youtube que oferece conteúdo gratuito sobre Estruturas de Dados, Sistemas Operacionais, Desenvolvimento Web, Jogos e outros assuntos para todos os profissionais e estudantes que desejam aprimorar seus conhecimentos e entender os fundamentos da Ciência da Computação e Desenvolvimento de Software. Palestrante na Campus Party, The Developer's Conference, QCon e outras conferências, Isidro está sempre ajudando as comunidades de desenvolvedores oferecendo conteúdo técnico e orientação profissional. 

  {pagebreak}
  ![](images/insercao_1.jpg)


================================================
FILE: manuscript/apendix-a.md
================================================
# Apêndice A: Segurança {#apendice_a}

## Práticas de Segurança

Código seguro deveria ser uma regra em qualquer design de aplicação, pois em alguns ramos são tão importantes quanto qualquer regra de qualidade que se possa ter. Para microsserviços não é diferente, e aqui não existe nenhum segredo, é a mesma regra para qualquer desenho de aplicação: no mínimo se deve olhar para o [Top 10 do OWASP](https://owasp.org/www-project-top-ten/) e seguir essas recomendações no seu projeto. É essencial que profissionais que trabalham com desenvolvimento conheçam as vulnerabilidades descritas no OWASP.

Sério mesmo, não há nada novo aqui, e qualquer pessoa que esteja tentando convencer você de um "Cross Microservice Injection" ou algo do tipo está tentando te enrolar. 

No caso de Microsserviços, a abordagem que muda é em relação a Autorização e Autenticação, e vamos discutir isso mais à frente.

Uma dica importante, em se tratando de código seguro, utilize as práticas de CI/CD para validar se há alguma vulnerabilidade no seu código. É interessante confirmar se as bibliotecas de terceiros que você planeja utilizar não trarão problemas ao ambiente.

E ferramentas para isso há várias, entre elas temos o [Fortify](https://www.microfocus.com/en-us/solutions/application-security), [Snyk](https://snyk.io/), [JFrog Xray](https://jfrog.com/xray/). Porque às vezes uma dependência desatualizada pode colocar seu serviço em posição de risco, então olhar a melhor prática no código e uma ferramenta para ajudar a apontar onde melhorar formam um time imbatível.

Outra prática que eu vejo em algumas empresas, principalmente quando os serviços estão expostos apenas internarmente e não para fora, é não usar o HTTPS, ou melhor, usar um TLS (Transport Layer Security). E para que você precisa disso? Para ter privacidade, integridade e identificação.

E quando estamos falando de microsserviços, um cenário que vai acabar sempre existindo diz respeito à necessidade de falar com servidores de autorização, e podemos estar falando de um API Key, ou de um "client secret", ou até mesmo de credenciais para uma autenticação básica. Então a primeira regra básica é: não deixe essas chaves no seu repositório de fonte, esses itens precisam ser variáveis de ambientes ou chaves de configuração externa, e elas devem estar sempre encriptadas.

Como estamos falando de containers, as práticas valem também para lá, e nunca rode seu container como "root". Você precisa assumir a premissa de que seu sistema nunca é 100% seguro, alguém vai conseguir explorar algo. Então você não pode só prevenir, você precisa detectar e reagir a isso.

A chave é seguir ao menos cinco pilares:

- Segurança por design (Secure by design);
- Vasculhe suas dependências;
- Use sempre HTTPS;
- Use Tokens de Acesso e Identidade;
- Proteja e criptografe seus segredos.

#### Soluções para Autenticação e Autorização

Para o mundo de microsserviços, o principal ponto é verificar quem você é (Autenticação) e aquilo que você pode fazer (Autorização). E dentro da arquitetura de microsserviços você vai estar espalhado em muito serviços pela rede e terá que lidar com alguns problemas em relação a como resolver isso.

Autenticação e Autorização precisam ser resolvidos em cada um dos microsserviços, e parte dessa lógica global vai ter que ser replicada em todos os serviços. Neste caso, um jeito para resolver isso é criar bibliotecas para padronizar essa implementação, só que isso vai fazer que você perca um pouco da flexibilidade de quais tecnologias usar, pois a linguagem ou framework precisa suportar essa biblioteca padrão.

O uso da biblioteca também ajuda a não quebrar o princípio da responsabilidade única, já que o serviço deveria se preocupar apenas com a lógica de negócio.

E outro ponto que é preciso analisar é que os microsserviços devem ser *stateless*, então é necessário usar soluções que consigam manter isso.

Podemos abordar a Autorização e Autenticação pelo modelo de sessão distribuída, usando ferramentas para você armazenar essa sessão. Você pode abordar/manter a sessão das seguintes maneiras:

**Sticky Session** - A ideia aqui é usar o load balancer e manter as conexões de origem sempre no mesmo servidor onde veio o primeiro request. Só que esta configuração só te permite escalar horizontalmente.

**Replicação de Sessão** - Toda instância salva a sessão e sincroniza através da rede. Só que aqui isso vai causar um "overhead" de rede. Quanto mais instâncias, mais será preciso replicar e lidar com a latência disso.

**Sessão Centralizada** - Isso significa que os dados podem ser recuperados em um repositório compartilhado. Em vários cenários, esse é um ótimo desenho, porque se pode dar alto desempenho para as aplicações, e você deixa o status do login escondido dentro dessa sessão. Mas claro que existe a desvantagem de que você precisa criar mecanismos para proteger essa sessão e replicar entre as aplicações, o que pode também adicionar latências na sua rede.

Mas quando estamos neste cenário de microsserviços, a recomendação passa a ser o uso de Tokens. A maior diferença para o modelo de sessão descrito acima é que deixamos de ter algo centralizado em um servidor e passamos a responsabilidade para a própria pessoa que vai utilizá-lo.

O Token vai ter a informação de identificação da pessoa (user) que está logada, e toda vez que chega ao servidor, podemos validar no server a identidade e a autorização. O token é encriptado e pode seguir um padrão como o [JWT](https://jwt.io/).

E usando token conseguimos delegar a responsabilide do estado da pessoa (user) logada, para algum processo que possa a validade do mesmo. Habilitamos vários tipos de validações de segurança que podem ser colocadas na malha (Service Mesh) ou no seu gateway de entrada e retirar essas responsabilidades dos serviços e aplicações e mesmo assim ainda continuar garantindo a segurança.

Com o uso do JWT, você passa a ter um "client token", e você vai passar a algum servidor para que ele possa fazer a validação/criação do mesmo. 

E quando se fala em Tokens, a chave é não querer reinventar a roda, e sim usar aquilo que já está consolidadado! É onde entra o OpenId e o OAuth/OAuth2. O Oauth 2 é praticamente o padrão mais utilizado para autenticação.

O padrão de OpenID é aquele usando quando você pode se conectar ou usar o token para se logar em vários sites ou serviços. Mas no seu padrão local, a recomendação é o OAuth, que estabelece um protocolo para que você tenha acesso aos recursos de que você precisa, e ele trabalha com quatro papéis:

**Resource Owner** - Esse é o papel que controla os acessos aos recursos.

**Resource Server** - É onde ficam os serviços a serem acessados, ou seja, aqui é onde estão as suas API's, aplicações e etc.

**Client** - É quem faz a solicitação ao Resource Owner do recurso que precisa consumir.

**Authorization Server** - Quem gera os tokens de acesso, permite que o Client chegue ao recursos que foram permitidos, com o nível de acesso definido.

E como funciona isso? Basicamente o "client" solicita ao Resource Owner acesso ao recurso, e este, quando autoriza, envia para o "Client" o "authorization grant", que é a credencial que representa que o Resource Owner autorizou a passagem. Então o "Client" vai solicitar ao Authorization Server um Token de acesso; tudo sendo válido, o Client recebe o seu token de acesso, que vai ser repassado para o Resource Server para que ele possa consumir aquilo que foi solicitado.

E existem 4 tipos de fluxos para obtermos o token de acesso no padrão OAuth. Temos o Authorization Code, que é o tipo mais comum; o Implicit, que é muito utlizado por aplicações SPA; o Resource Owner, que estabelece a confiança entre as aplicações; e o Client Credentials, que é usado para falar de um serviço para outro.

Tirando a parte de Autenticação e Autorização, que precisam de um cuidado à parte, a segurança de microsserviços é como a segurança de qualquer aplicação e precisamos prestar atenção a isso.

================================================
FILE: manuscript/bibliography.md
================================================
# Bibliografia



- [Java Efetivo](https://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/ref=pd_lpo_14_t_0/137-8030169-2504860?_encoding=UTF8&pd_rd_i=0321356683&pd_rd_r=b3c8dab8-c0c9-4a4d-a018-a3ca61d90b58&pd_rd_w=NUGFI&pd_rd_wg=kRfae&pf_rd_p=7b36d496-f366-4631-94d3-61b87b52511b&pf_rd_r=CJ8160X0PF9Z2VMM0FQC&psc=1&refRID=CJ8160X0PF9Z2VMM0FQC);   
- [Clean Code](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=sr_1_1?dchild=1&keywords=clean+code&qid=1600030324&s=books&sr=1-1);
- [Domain Driven Design (DDD)](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?dchild=1&keywords=DDD&qid=1600030336&s=books&sr=1-1): 
  - [Implementing DDD](https://www.amazon.co.uk/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577) 
  - [DDD Distilled](https://www.amazon.co.uk/Domain-Driven-Design-Distilled-Vaughn-Vernon/dp/0134434420/ref=pd_lpo_14_t_1/262-0200870-8496500?_encoding=UTF8&pd_rd_i=0134434420&pd_rd_r=c7957a5b-3f2f-4008-8c93-8a9b5792c448&pd_rd_w=JKKyX&pd_rd_wg=dFALp&pf_rd_p=7b8e3b03-1439-4489-abd4-4a138cf4eca6&pf_rd_r=W41G9RPNEBHEF8Y5DXG8&psc=1&refRID=W41G9RPNEBHEF8Y5DXG8)
  - [The Business Value of Using DDD](https://www.informit.com/articles/article.aspx?p=1944876&seqNum=4)
  - [Domain Driven Design Quickly](https://www.infoq.com/minibooks/domain-driven-design-quickly/)
- [Clean Architecture](https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=sr_1_1?crid=V32SL0V5C426&dchild=1&keywords=clean+architecture&qid=1600030309&s=books&sprefix=clean+a%2Cstripbooks-intl-ship%2C270&sr=1-1);
- [Building Microservices](https://www.amazon.com/Building-Microservices-Designing-Fine-Grained-Systems/dp/1491950358/ref=sr_1_1?dchild=1&keywords=Building+Microservices&qid=1600030352&s=books&sr=1-1);
- [The Twelve Factors](https://12factor.net/);
- [Monolith to Microservices (Sam Newman)](https://www.amazon.com/Monolith-Microservices-Evolutionary-Patterns-Transform/dp/1492047848/ref=sr_1_1?dchild=1&keywords=Monolith+to+Microservices&qid=1600030381&s=books&sr=1-1)
- [Fundamentals of Software Architecture: an Engineering Approach](https://www.amazon.com/Fundamentals-Software-Architecture-Comprehensive-Characteristics/dp/1492043451/ref=sr_1_1?crid=15A3MVYFJWK4G&dchild=1&keywords=arquitectura+de+software&qid=1600030589&s=books&sprefix=arquitectura%2Cstripbooks-intl-ship%2C272&sr=1-1)
- [Cloud-Native Continuous Integration and Delivery: Build, test, and deploy cloud-native applications in the cloud-native way](https://www.amazon.com.br/gp/product/B07HHDVSJK?ref_=kcp_mac_dp), Onur Yılmaz
- [Kubernetes Patterns: Reusable Elements for Designing Cloud-Native Applications](https://www.amazon.com.br/Kubernetes-Patterns-Designing-Cloud-Native-Applications-ebook/dp/B07QH3JCC6/ref=sr_1_1?__mk_pt_BR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=kubernetes+patterns&qid=1591755073&s=digital-text&sr=1-1), por Bilgin Ibryam, Roland Huß
- [Teaching an Elephant to Dance: Intentional evolution across teams, processes, and applications](https://www.redhat.com/cms/managed-files/mi-middleware-teaching-elephant-to-dance-ebook-f8980kc-201709-en.pdf), por Burr Sutter e Deon Ballard
- [Bigtable: a Distributed Storage System for Structured Data](https://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf), by Fay Chang, Jeffrey Dean, Sanjay Ghemawat, Wilson C. Hsieh, Deborah A. Wallach, Mike Burrows, Tushar Chandra, Andrew Fikes, Robert E. Gruber

================================================
FILE: manuscript/chapter_01.md
================================================
# A importância de aprender conceitos ao invés de novos frameworks {#chapter_01}

Escrever este capítulo pode não ser uma das tarefas mais fáceis, uma vez que o público-alvo deste material é o de desenvolvedores(as) com alto nível de experiência (*Senior Developers*). A intenção, portanto, deste texto não é de um acadêmico dizer o que profissionais devem estudar, mas, principalmente, gerar uma autocrítica sobre muitos assuntos que poderiam ser explorados com maior profundidade (e até mesmo a aplicação da famosa expressão "ligar os pontos") enquanto estávamos em processo de formação (na universidade).

Pois bem, como iniciar? Basicamente, a formação de profissionais de desenvolvimento tem como base seu *background* em um curso de Ciência (ou Engenharia) da Computação (ou outro bacharelado na área). Muitas dessas respostas podem estar em disciplinas já esquecidas por nós, como estudantes, e mesmo por profissionais. 

O grande "mistério" que podemos tentar revelar aqui neste capítulo pode ser extremamente simples (ou tornar-se simples) a partir do momento em que identificamos o seguinte: as diferentes disciplinas que estudamos estão sempre interconectadas, mesmo que não pareça.

## Por que estudar Análise de Algoritmos, Estruturas de Dados, Sistemas Operacionais, Arquitetura de Computadores, Grafos, entre outros?

Podemos interconectar disciplinas de Análise de Algoritmos com Programação Orientada a Objetos e Bancos de dados? Sim! E de uma forma mais comum do que se pode imaginar:

Considere o exemplo de uma busca de objetos em um banco através de um ORM. Dependendo da aplicação, compartilhamento de recursos, escalabilidade e também concorrência, temos que pensar em eficiência de algoritmos mesmo antes de sua implementação. 

Ter consciência de que um `Lazy Fetch` com Hibernate, por exemplo pode gerar uma complexidade assintótica muito maior do que um `Eager Fetch` é fundamental para senior developers. Isso porque, por exemplo, em um sistema de recuperação de cabeçalhos de pedidos e seus respectivos itens, ocorre uma sequência de acessos ao banco de dados muito volumosa na primeira abordagem (*lazy*). Acessos para recuperar todos os cabeçalhos de pedidos e, para cada pedido, mais uma sequência de acessos ao banco de dados. Se observarmos um algoritmo com essas características, veremos algo próximo ao pseudocódigo a seguir:

	recuperar conjunto de pedidos e armazenar em listaP
	para cada pedido P em listaP faça
		recuperar itens I[] do pedido P a partir de seu ID
	fim-para

{pagebreak}

De forma bastante geral, a execução das instruções pode ser descrita da forma abaixo:

	1 execução da linha 1
	N+1 execuções da linha 2 (for sempre tem o teste que retorna falso)
	N execuções da linha 3

Desse modo, a complexidade desse algoritmo é de ordem O(n). Isso significa que, à medida que o conjunto de valores recuperados na linha 1 aumenta, o tempo computacional aumenta proporcionalmente.

Podemos extrapolar ainda esse algoritmo e, para cada item, recuperar toda a lista de impostos, que pode gerar um algoritmo de complexidade quadrática O(n2). Observe:

	recuperar conjunto de pedidos e armazenar em listaP
	para cada pedido P em listaP[] faça
		recuperar itens I[] do pedido P a partir de seu ID
	  para cada item I de P, faça
		recuperar lista L de impostos de I[]
	  fim-para
	fim-para

Nesse caso, analisando a complexidade assintótica, temos:

	1 execução da linha 1
	N+1 execuções da linha 2
	N execuções das linhas de 3 a 5, o que implica:
		1 execução da linha 3
		N+1 execuções da linha 4
		N execuções da linha 5

Portanto, se somarmos todas as execuções, temos 1 + (N+1) + N * (1 + (N+1) + N), o que resulta num polinômio cujo maior expoente é 2 (Nˆ2). Logo, nosso algoritmo de busca de pedidos, itens e impostos pode ser considerado um algoritmo de ordem quadrática. 
Isso porque apenas pensamos na recuperação da informação. Nem pensamos ainda na iteração desses valores, o que pode elevar ainda mais o tempo computacional. Agora imagine esse cenário em um sistema Web com algumas dezenas de milhares de pessoas conectadas. Se fizermos acessos ao disco (que é um dispositivo bastante lento, se comparado ao acesso à memória) de forma ineficiente para uma única requisição, como será o desempenho da aplicação para diversas requisições simultâneas?

A que conclusão podemos chegar depois de toda essa discussão? Talvez não seja tarefa do dia a dia de senior developers realizar a análise assintótica de algoritmos através da obtenção dos polinômios. Porém, para quem irá trabalhar com problemas mais complexos que a maioria dos casos do dia a dia, identificar complexidades (mesmo que de forma mais superficial, inicialmente) de algoritmos é fundamental para ter uma visão criteriosa de quais decisões tomar.

## A importância de um estudo mais aprofundado em disciplinas teóricas
Sabemos que Estruturas de Dados é um assunto que, para muitos(as) estudantes em formação, é tenebroso e extremamente abstrato. Porém é necessário e fundamental termos conhecimento das estruturas básicas e avançadas para também entendermos as ferramentas que utilizamos em sistemas atuais.

Quer exemplos? 

- **Filas**: Apache Kafka, Rabbit MQ, MQTT, entre outros, são exemplos de ferramentas que utilizam os conceitos de `filas` em suas implementações. Não apenas filas únicas, mas filas com prioridades, que também são objeto de estudo no período da graduação.
- **Pilhas**: Sua IDE faz uso de pilhas a todo momento para conferir se as chaves abertas foram fechadas corretamente (o *parser* da linguagem faz uso de pilhas quase que constantemente). Se os parênteses que definem as instruções em `Clojure` estão em conformidade. Isso apenas para citar exemplos imediatos. 

Além desses exemplos mais "simples" (e destaco a importância de se colocar o termo entre aspas), temos também exemplos mais complexos, que geram muita diferença de desempenho nas aplicações. Por exemplo: qual a diferença entre utilizarmos Listas (sejam elas Vetores ou listas ligadas) e Hash? A principal diferença está no desempenho da busca. Observe este pequeno exemplo de benchmark em Java:

```java
	ArrayList<Produto> lista;
	lista = new ArrayList<Produto>();
	for (int i = 0; i < 100000; i++) {
	    Produto p = new Produto(i + 1, "Produto " + (i + 1), 0, 0);
	    lista.add(p);
	}
	int quantosAchei=0; // numero de ocorrencias encontradas
	// inicio da medicao do tempo
	long inicio = System.currentTimeMillis();
	Produto busca;
	for (int cont = 0; cont < 10000; cont++) {
	    for (int i = 0; i < lista.size(); i++) {
	        Produto tmp = lista.get(i);
	        if (tmp.getId() == -1) { // forcando buscar um ID que nao existe na lista -> o pior caso
	             busca = tmp;
	             quantosAchei++;
	             break;
	         }
	     }
	 }
	 long fim = System.currentTimeMillis();
	 // fim da medicao do tempo
	 System.out.println("Achei = " + quantosAchei +" em "+(fim-inicio));
```

Esse simples algoritmo popula uma lista com 100.000 objetos do tipo produto e realiza 10.000 buscas de um produto inexistente nessa lista. Como é uma estrutura linear (e voltamos à análise de algoritmos), a busca obrigatoriamente tem que passar por todos os objetos até concluir que ele não existe na lista. Um algoritmo deste pode levar alguns bons segundos para executar (um teste em uma máquina comum pode levar entre 2 e 5 segundos para executar).
É possível melhorar o desempenho dessa busca? Claro que sim. Poderíamos usar, ao invés de busca linear, um algoritmo de busca binária. Entretanto, para essa estratégia, nosso conjunto precisaria estar previamente ordenado. 

Agora, se pensarmos em outra estrutura, como um mapa Hash, qual a vantagem? Vamos observar este código.

		HashMap<Integer, Produto> mapa;
		mapa = new HashMap<Integer, Produto>();
		for (int i = 0; i < 1000000; i++) {
		   Produto p = new Produto(i + 1, "Produto " + (i + 1), 0, 0);
		   mapa.put(p.getId(), p);
		}
		int quantosAchei=0;
		
		// inicio da medicao
		long inicio = System.currentTimeMillis();
		for (int cont=0; cont< 10000; cont++) {
			Produto busca = mapa.get(-1); // novamente forcando a busca de um elemento que nao existe
			if (busca != null) {
				quantosAchei++;
			}
		}
		long fim = System.currentTimeMillis();
		// fim da medicao
		System.out.println("Achei = "+quantosAchei+" em "+(fim-inicio));

Pela própria definição de Hash, há um cálculo para determinar, através de um atributo-chave do objeto, qual a posição de memória que ele irá ocupar. Uma vez determinada essa posição, o acesso é direto ao objeto (ou à inexistência dele). Portanto, o tempo computacional de acesso de um objeto em um mapa hash é O(1), ou seja, constante!

Desse modo, independentemente do tamanho do conjunto, o tempo de acesso sempre será o mesmo (o que traz um desempenho excepcional, se comparado ao desempenho de busca em uma lista). 

Não somente Hash, como também Árvores binárias, AVLs, ou mesmo B-Trees, para que dev sêniors tenham condições mínimas de definir estratégias de índices em tabelas de bancos de dados relacionais.

Agora, estou buscando valorizar bastante a Análise de Algoritmos, correto? Quer um exemplo de onde a Análise de Algoritmos pode não ser suficiente para uma solução?

Um algoritmo bastante comum na formação durante a graduação é o percurso e preenchimento de matrizes. Esse tipo de algoritmo, independente de o preenchimento ser via Linhas x Colunas ou Colunas x Linhas, na visão única e exclusiva da Análise de Algoritmos é irrelevante, pois ambos são de ordem assintótica O(nˆ2). Observe estes fragmentos de código Java:

**Fragmento 1**: preenchimento Linha x Coluna


	for (int i=0; i < TAM; i++)
		for (int j=0; j < TAM; j++)
			matriz[i][j] = valor;

**Fragmento 2**: preenchimento Coluna x Linha

```
for (int i=0; i < TAM; i++)
	for (int j=0; j < TAM ; j++)
		matriz[j][i] = valor;
```

O aspecto interessante nesses códigos é que, na prática, o desempenho desses dois algoritmos é muito diferente, se a dimensão da matriz for considerável (aqui, para considerável, vamos pensar em valores acima de 5.000).

Mas por que a diferença de desempenho? Por conta do número de acessos ao cache. Se aprofundarmos um pouco mais o estudo e também levarmos em conta a Arquitetura de Computadores, veremos que uma matriz é alocada em memória como um grande array (a notação de matriz que as linguagens de programação utilizam é apenas uma abstração). Por conta disso (e levando em consideração o conceito da localidade referencial - negligenciado no estudo de Sistemas Operacionais), obviamente, se uma linha é trazida para o cache, é muito mais rápido preencher seus elementos adjacentes até que haja a necessidade de acesso a outras páginas de memória (gerando assim um *page fault*) do que ficar buscando elementos em regiões "distantes" da memória, aumentando - e muito - o volume de paginação.

Aliás, pode não parecer algo muito próximo do desenvolvimento Web atual levantarmos aspectos de Sistemas Operacionais e Arquitetura de Computadores, mas muitos desses conceitos de gerenciamento de memória e algoritmos de substituição de páginas estão, sim, presentes em ferramentas de desenvolvimento Web. Ferramentas de Cache (como REDIS, por exemplo) precisam definir uma política de atualização desses dados que serão expostos. Conhecer os algoritmos como o *MRU - Most Recently Used*, *LRU - Least Recently Used*, *MFU - Most Frequently Used*, entre outros, pode ser de fundamental importância para entender como uma ferramenta dessas trabalha, ou mesmo (na ausência de algo pronto) para implementar sua própria ferramenta de cache.

E o estudo de teoria dos Grafos? Sem os algoritmos de otimização vistos em Grafos (como o famoso *Algoritmo de Dijkstra*), não teríamos aplicações como Waze, por exemplo. Só que o ponto principal não se limita a apenas reproduzir o algoritmo, mas principalmente em encontrar um modelo que represente o que arestas e vértices significam no contexto do problema a ser resolvido. A distância entre dois vértices pode ser interpretada através da diferença efetiva em metros (ou quilômetros) ou, se baseada na velocidade dos automóveis e no tempo para se trafegar entre dois pontos, infere-se uma distância que pode ser dinâmica e, por consequência, atualizar o valor das arestas.

Não apenas Grafos, mas Autômatos e Compiladores, duas disciplinas fundamentais hoje para extração de textos através de expressões regulares (100% implementadas através de Autômatos Finitos Determinísticos - os famosos AFDs) e também Compiladores para a construção de novas linguagens (se observarmos a quantidade de linguagens que surgiram desde o ano 2000 até hoje, isso é muita coisa).

Mas você, como dev, pode se perguntar: "E quando eu farei uma nova linguagem?". Talvez a resposta seja "dificilmente", para não dizer "nunca"; mas, olhando um pouco além do dia a dia, desenvolvedores Front-End já devem ter utilizado frameworks como Angular ou React, correto? Pois bem, tais frameworks precisam fazer uso de análise de arquivos HTML com *tags* específicas que substituem valores para objetos. A leitura dessas tags (para posterior geração do código final da sua *single page application*) é feita por um *parser* que, a partir dos componentes criados, gera todo o arquivo HTML com as bibliotecas Javascript para que seu Aplicativo consiga funcionar. Talvez isso ajude a responder à questão que motiva este capítulo, na seção a seguir. 

## O ponto-chave: usar frameworks, fazer as tarefas manualmente, ou melhor, criar seu próprio?

Difícil de responder, porém com uma tendência a buscar desafios. Profissionais de desenvolvimento tornam-se sênior com mais rapidez quando entendem profundamente como as tecnologias, bibliotecas e frameworks funcionam nos bastidores. Portanto, tentaremos responder a essa pergunta (ou talvez até uma provocação) analisando-a por três diferentes visões.

**Visão do conhecimento**: Obviamente, você pode (e deve!) criar seu próprio framework pelo menos uma vez na vida, mesmo que a única pessoa a usar esse framework seja você. Qual o motivo? Este mesmo: conhecimento. Silvio Meira (um dos principais influenciadores em Computação do Brasil e do mundo) já disse em uma palestra que "*o profissional de computação que nunca desenvolveu um compilador não pode se considerar um profissional completo, pois um compilador exige que você conheça toda a grade de um curso de Ciência da Computação*". E ouso complementar: desenvolva também uma *Game Engine*, pois existe a parte de Álgebra Linear e Computação Gráfica que é pouco aplicada em um compilador. Um compilador, assim como uma Game Engine, exige que um dev *Ligue os Pontos* conceituais que sempre ficaram negligenciados.

**Visão Corporativa**: Muitas empresas podem adotar frameworks feitos pela comunidade? Sim, claro! Entretanto, existem decisões estratégicas dentro das corporações para que seus produtos não dependam de tecnologias de terceiros. Portanto, é muito comum haver times de desenvolvimento de frameworks (nos grandes *players*, principalmente) para uso interno em seus produtos. Isso proporciona controle total sobre a tecnologia que empregam. Quando esses frameworks estão maduros o suficiente, podem ficar disponíveis para a comunidade (em licenças *Open Source*) para que a comunidade faça bom uso e os popularize. Ou simplesmente ficam para uso interno e exclusivo, sem acesso pelas comunidades de desenvolvimento.

**Visão da Necessidade**: Este pode ser um agente motivador mais imediato, pois criar um framework tem por objetivo aumentar a produtividade no desenvolvimento de software. Quando os processos internos de desenvolvimento de equipe são bem definidos, é óbvio que os times começam a enxergar as necessidades comuns e gerar soluções de uso geral e propósito específico, sejam eles frameworks Back ou Front end, ORM ou mesmo em mais baixo nível para geração ou otimização de código. O principal objetivo de se construir uma tecnologia como essa é justamente investir tempo criando uma ferramenta que irá otimizar o tempo de desenvolvimento de uma aplicação (compare o tempo de desenvolvimento de uma camada de integração de Java com Bancos de Dados utilizando JDBC puro ou JPA, se ainda restam dúvidas).

Portanto, e para concluir, à medida que profissionais se tornam naturalmente seniores, seu conhecimento sobre as mais diversas áreas do conhecimento dentro da Ciência da Computação tende a tornar-se mais profundo e ao mesmo tempo mais amplo. Não basta conhecer um pouco sobre várias coisas e especializar-se em apenas uma delas. À medida que os problemas difíceis passam a ser mais comuns, profissionais tendem a ser expostos a situações que exigem muito conhecimento em muitas disciplinas diferentes, bem como a maneira como esses conhecimentos estão relacionados para a resolução do problema. O famoso "*ligar os pontos*".

================================================
FILE: manuscript/chapter_02.md
================================================
# Tenho lido sobre DDD, para onde devo ir depois? {#chapter_02}

Você já codifica Java há muitos anos, já leu sobre Domain-Driven Design (DDD) e deseja aplicar isso a um projeto da vida real. Como isso funciona? O que realmente significa aplicar DDD no ecossistema de hoje? Realmente vale a pena o tempo investido?

O DDD nos fornece uma base teórica e padrões muito bons para construir um software robusto, que agregue valor real ao negócio. Para fazer isso, o DDD ajuda a fornecer uma estrutura para organizar o software construído e otimizar equipes independentes, que possam trabalhar juntas consumindo APIs bem definidas. No final das contas, o DDD fornece os princípios de que você precisa para traduzir para Java ou qualquer outra linguagem de sua escolha. Mas, ao fazer isso, você precisará tomar algumas decisões difíceis sobre como esses componentes/serviços/aplicações Java serão executados e interagirão.

Este capítulo aborda o lado prático de como esses conceitos podem ser mapeados em um exemplo existente, executado no Kubernetes, oferecendo dicas práticas sobre como os conceitos DDD podem ser mapeados para um stack tecnológico (pilha de tecnologia) concreto. Claro, existem milhares de opções diferentes para se escolher hoje, mas você pode tomar isso como um exemplo do tipo de coisas a que você precisará se atentar ao passar por esta jornada.

> **INFO:** é importante destacar que este capítulo não é sobre os conceitos básicos de DDD. Portanto, se você é novo no DDD, os seguintes livros são recomendados: Implementing DDD e DDD Distilled.

{pagebreak}

Este capítulo está dividido em duas seções principais:

- [Introdução aos tópicos relacionados a Java e nuvem](#java-na-nuvem)
- [De Monolith até K8s usando DDD](#evoluindo-seu-monolito)


## Java na nuvem {#java-na-nuvem}

Existem muitos frameworks por aí com o objetivo de fornecer uma experiência mais fácil para os desenvolvedores que criam microsserviços. Exemplos disso são Spring Boot, Quarkus, Micronaut, Helidon etc. Em geral, o objetivo desses frameworks é criar um arquivo JAR autônomo, que possa ser executado, fornecendo a você um executável livre de dependências e que requer apenas a Java Virtual Machine (JVM) para rodar.

Isso vai contra o que nós (como comunidade Java) estávamos fazendo cinco anos atrás, e alguns de nós ainda fazem, que era fazer o deploy de nossas aplicações Java dentro de um servidor de aplicação ou um Servlet Container como o Tomcat.

Enquanto costumávamos ter um monolito com todos os recursos de nossas grandes aplicações integradas, agora buscamos ter um conjunto de serviços com funcionalidades bem definidas. Esses novos (micro) serviços compartilham as seguintes características:

- Tendem a localizar e versionar todos os artefatos que são necessários para ir do código-fonte até um serviço em execução em um ambiente.
- Cada serviço é construído, mantido, desenvolvido e implantado por uma equipe diferente.
- Cada serviço tem seu próprio ciclo de lançamento.
- Cada serviço expõe um conjunto bem definido de APIs.

Construir um serviço hoje com endpoints REST é uma tarefa bastante fácil se você estiver usando um desses frameworks mencionados anteriormente. Você tem um modelo de programação baseado em anotação que permite mapear métodos Java para terminais REST e mecanismos avançados de serialização/desserialização que lidarão com todo o boilerplate de análise de solicitações HTTP.

> **DICA:** Para mais detalhes sobre a arquitetura de microsserviços, consulte o capítulo [Microsserviços](#chapter_07).

O verdadeiro problema surge quando você começa a ter mais do que um punhado de serviços. A execução de cada serviço em sua própria JVM irá forçá-lo a executar cada serviço em uma porta diferente e cuidar dos problemas quando essas JVMs travarem. Por isso, a indústria saltou rapidamente para containers por volta de 2015.


### Containers & organização de containers

Seguindo a mesma linha de ficar livre de dependências, como os frameworks Java mencionados na seção anterior, containers nos ajudam a executar nosso software em qualquer lugar. Isso significa que vamos um passo adiante e agora não queremos nem mesmo depender da Java Virtual Machine sendo instalada em nossa máquina host, onde os serviços serão executados.

Docker (um container runtime) nos ajudou com isso. Podemos encapsular nosso JAR autônomo junto com a JVM e os arquivos de configuração para torná-lo um container que possa ser executado em qualquer lugar onde o Docker Container Runtime estiver instalado.

Cada container Docker tem seu próprio tempo de execução isolado, o que nos permite isolar as falhas nos limites do container Docker.

Quando você tem um Bounded Context (contexto delimitado) e alguns Serviços, provavelmente está tudo bem apenas executar esses microsserviços Java usando um script (ou docker-compose). No entanto, mesmo para as práticas diárias de desenvolvimento, você notará que cada um desses serviços exigirá uma porta dedicada, e será necessário manter o controle dessas portas para garantir que estejam disponíveis para cada um dos serviços.

Quando o número de serviços aumenta, isso se torna incontrolável.
Por esse motivo, os Container Orchestrators (orquestradores de container) se tornaram populares nos últimos anos, e o Kubernetes está liderando o caminho. O Kubernetes é responsável por lidar com a criação desses container runtimes, como escaloná-los quando há carga e como lidar com containers que apresentem mau comportamento ou falhem.

> **DICA:** Para obter mais detalhes sobre containers e ferramentas de orquestração, consulte o capítulo sobre [Cloud](#chapter_08).

O sucesso do Kubernetes é baseado no fato de que cada grande provedor na nuvem fornece um serviço Kubernetes gerenciado, tornando-o o padrão de fato para suporte a multicloud. Em outras palavras, não importa qual provedor você escolha, você sempre pode confiar que haverá uma API Kubernetes exposta para você interagir e provisionar seus serviços.


### Capitalizando os benefícios do DDD

Se você seguir o caminho do DDD, precisará primeiramente aproveitar algumas das promessas que o DDD lhe deu e certificar-se de que está colhendo os benefícios. Se não podemos fornecer continuamente novas versões de nossos serviços sem interromper a aplicação inteira, estamos apenas tornando nossa vida mais complicada para nada. Se não estamos entregando valor de negócio concreto como resultado do DDD, todas as mudanças sugeridas neste capítulo não valem o esforço ou o tempo.

Eu recomendo o seguinte artigo: [“The Business Value of Using DDD”](https://www.informit.com/articles/article.aspx?p=1944876&seqNum=4), que dá uma visão geral de alto nível dos benefícios de adotar o DDD, não para você como desenvolvedor(a), mas para o seu negócio.

A próxima seção explora um exemplo que eu criei com base na minha experiência durante a rearquitetura de aplicações monolíticas Java para uma abordagem mais distribuída. O exemplo é fictício, qualquer semelhança com a realidade é mera coincidência :) Nós encorajamos você a abstrair os conceitos e padrões do cenário de exemplo e mapeá-los para seu próprio domínio. No final das contas, este é apenas um exemplo, embora complexo e totalmente funcional.


## Evoluindo seu monolito na prática, usando DDD {#evoluindo-seu-monolito}

Esta seção cobre um cenário de exemplo que nos ajuda a explicar alguns dos conceitos em ação. Você pode mapear esses conceitos para seu domínio de negócios e copiar a solução técnica real do exemplo para alguns dos desafios apresentados.

Como esperado, criar uma aplicação completa é um trabalho árduo e geralmente requer muito tempo. Por esse motivo, o exemplo a seguir é fornecido como um conjunto de repositórios de código aberto, e você pode contribuir para melhorá-lo.

- [Repositório De Monolith para K8s Github](https://github.com/salaboy/from-monolith-to-k8s)

> **DICA:** Como uma aplicação real, o exemplo evoluirá com o tempo, agregando mais ferramentas e melhores práticas, por isso convidamos você a participar dessa jornada em que todos podemos aprender e compartilhar informações valiosas juntos.

Começamos nossa jornada com uma aplicação Java monolítica. O cenário que iremos abordar pertence a uma empresa que se encarrega de fornecer uma plataforma para a criação de sites de conferências. Imagine que cada um de nossos clientes exija que hospedemos e escalonemos seu site de conferências.
Todos nós vimos grandes aplicações Java Web e, neste cenário, a aplicação se parece com isto:

![Imagem 02_01: Aplicação monolítica para gerenciamento de conferências.](images/chapter_02_01a.png)

O Facade (fachada) “Customer Management” se encarrega de isolar os diferentes clientes uns dos outros. Em algumas empresas, isso é definido como uma plataforma ou aplicação multi-tenant (multilocatário). Infelizmente, isso é bastante comum no espaço Java. Por razões históricas, as implementações acabaram crescendo em monolitos grandes e assustadores, que vieram com muitos problemas de escalabilidade, bem como com desafios de isolamento de tráfego e dados. Não importa o quão sofisticada seja a aparência de nossa plataforma, ela está apenas rodando em uma única JVM. Você não tem como dimensionar cada cliente individualmente - você dimensiona tudo ou nada.

Como você pode ver na caixa vermelha, cada Site de Conferência conterá um monte de módulos, dependendo da seleção de cada cliente, mas, na realidade, em tempo de execução, todo o código estará lá para cada conferência.

> Observe que, se você tiver uma plataforma como esta e ela fizer o trabalho que deve fazer, você **NÃO deve** alterá-la. A menos que você esteja tendo problemas para gerenciar ou escalar essa "plataforma", você não deve rearquitetar a coisa toda.

Agora, essa arquitetura de monolito tem algumas desvantagens claras e, para esse cenário, podemos considerar o seguinte motivo para rearquitetá-la em uma plataforma adequada nativa da nuvem:

- Clientes não podem ser escalados independentemente.
- O tráfego de clientes é todo gerenciado pela mesma aplicação.
- Ponto Único de Falha na JVM.
- Se os dados são armazenados em um banco de dados, os dados são compartilhados entre os(as) clientes. O código da aplicação precisa lidar com o isolamento dos dados de cada cliente. O banco de dados também se torna um gargalo, pois todos os dados dos(as) clientes estão no mesmo banco de dados.
- Cada mudança na plataforma requer que toda a aplicação seja reiniciada.
- Cada desenvolvedor(a) envolvido(a) com a aplicação trabalha com a mesma base de código, o que torna um lançamento e uma fusão de recursos uma grande tarefa, com muitos riscos envolvidos. Isso geralmente pode ser feito por alguém que entenda toda a aplicação.

> **DICA:** Se você já tem essa aplicação instalada e funcionando e tem clientes usando a plataforma, terá um bom entendimento de quais recursos são essenciais e como começar a reformulá-los.

Como Martin Fowler descreve no post [Monolith First](https://martinfowler.com/bliki/MonolithFirst.html), é o caminho a tomar. Por ter um monolito, você já entende a solução que precisa construir, tornando mais fácil estimar como a nova arquitetura resolverá os problemas da versão existente. Em outras palavras, se você não tem um monolito existente, não comece com uma arquitetura distribuída do zero. Crie um monolito primeiro e depois divida, se necessário.

O próximo passo em nossa jornada é decidir por onde começar. Em minha experiência, vi três padrões comuns se repetindo:

- **Iniciar novas funcionalidades como serviços separados**: isso geralmente é recomendado se você puder manter o monolito como está. Novos serviços não resolverão os problemas já existentes, mas ajudarão suas equipes de desenvolvedores a se acostumarem a trabalhar com uma mentalidade de microsserviços.
- **Dividir a funcionalidade existente do monolito** (e lentamente desativar o código antigo): se você tiver problemas urgentes com o monolito, pode avaliar a ramificação de algumas das funcionalidades externas para um novo serviço. Isso pode resolver alguns dos problemas existentes, mas não trará nenhum valor de negócios imediatamente. Isso também aumenta a complexidade das operações do dia a dia, pois você pode acabar executando duas soluções para o mesmo problema por um longo período de tempo. Além do mais, isso pode ser usado para entender o quão complexa e cara uma rearquitetura central pode ser.
- **Reestruturar o núcleo da plataforma como microsserviços** (para resolver os problemas existentes): mais cedo ou mais tarde, se você estiver tendo problemas para manter e dimensionar seu monolito, precisará repensar e redesenhar as partes principais de sua plataforma, certificando-se de focar em resolver os problemas atuais de escalabilidade e manutenção. Isso pode ser uma iniciativa cara, mas pode ser feito de forma totalmente isolada de seus ambientes de produção. Várias vezes eu vi como isso é feito como uma Prova de Conceito para demonstrar que é realmente possível e também para garantir que os membros de sua equipe entendam as implicações de um negócio (vantagens) e do ponto de vista técnico (novo stack tecnológico, novas ferramentas).

Neste capítulo, irei cobrir a última dessas opções (**Reestruturar o núcleo da plataforma como microsserviços**) para destacar a solução para nossos problemas existentes com a aplicação monolítica, mas você pode explorar as outras duas, se elas forem mais apropriadas para sua situação.

> **DICA:** Mais informações sobre estratégias e práticas sobre como migrar um monólito existente para a arquitetura de microsserviços podem ser encontradas no capítulo [Microsserviços](#chapter_07.md).


É aqui que os conceitos e padrões DDD se tornam realmente úteis para definir como dividir as funcionalidades do monolito e como organizar nossas equipes em torno dos novos serviços. Nas seções a seguir, exploraremos alguns desses conceitos em ação.


### Suposições e simplificações

As plataformas são muito complexas. Tentar reestruturar algo grande vai lhe dar mais dores de cabeça do que soluções. Por esse motivo, você deve tentar simplificar o escopo do problema para enfrentar diferentes desafios de forma controlada.

Para o nosso cenário, isso pode significar que, em vez de tentar a rearquitetura de toda a plataforma, primeiro tentaremos resolver a arquitetura e a forma de um simples site de conferências. Isso significa que, em vez de focar a plataforma, primeiro nos concentraremos no que o cliente espera de seu site de conferência. Ao compreender a forma de um único site de conferência, você e suas equipes têm um conjunto bem definido de desafios a serem resolvidos, o que pode agregar valor imediato aos negócios. Posteriormente, automatizaremos como cada um desses sites de conferências pode ser provisionado.

Do ponto de vista arquitetônico, isso pode significar um site de conferência monolítico ou um site de conferência construído com diferentes serviços distribuídos. Se os sites de conferência forem complexos o suficiente e quisermos reutilizar os módulos para todos eles, podemos considerar uma abordagem distribuída. Com base na minha experiência, esse tipo de plataforma aproveita os serviços compartilhados na maioria das vezes, então faz sentido arquitetá-los pensando na reutilização.

> **DICA**: Essa estratégia leva a vários serviços desde o primeiro dia, então isso é algo com o qual você e suas equipes devem se acostumar.

{pagebreak}

Nossos sites de conferências independentes serão semelhantes a este:

![Imagem 02_02: Evolução da aplicação de conferências monolítica para uma arquitetura com serviços distribuídos.](images/chapter_02_02.png)

Como você pode ver no diagrama anterior, está bastante claro que há mudanças arquitetônicas importantes. É bastante comum ter a Interface do Usuário, neste caso, a caixa “Conference Site”, separada dos serviços principais. Na maioria das vezes, esse componente voltado para o(a) usuário(a) agirá como um roteador, encaminhando solicitações do site de conferência para serviços que não são expostos diretamente a usuários(as).

Mas, antes de entrar em detalhes técnicos, como priorizamos e nos certificamos de que começamos com o pé direito? Como podemos definir o escopo desses serviços corretamente (nem muito macro, nem muito micro)? Os conceitos DDD podem nos ajudar, fornecendo algumas respostas e orientações.

{pagebreak}

### Contextos limitados para começar a dividir seu monolito

Se você está planejando uma rearquitetura para o núcleo de sua plataforma, precisa ter cuidado e escolher com sabedoria quais componentes serão rearquitetados primeiramente e por quê.
Na minha experiência, você deve tentar lidar com os componentes principais primeiro. Escolha componentes que você entende e sabe que agregam valor ao negócio onde você está.

No caso do nosso cenário, lidar com a Chamada de Propostas (Call for Proposals) é fundamental para iniciar uma conferência.
Há um trabalho substancial para aceitar propostas, revisá-las e garantir que a conferência tenha uma agenda interessante.

Se estivermos confiantes de que a implementação da funcionalidade de Chamada de Propostas primeiro nos dará vitórias imediatas, precisamos estimar o quão grande e complexo é o esforço.

O DDD propõe o conceito de contexto limitado como um conjunto bem definido de funcionalidades que se encaixam. Essas funcionalidades geralmente mapeiam como um especialista de domínio (especialista no assunto) fará o trabalho se não houver software disponível. Para planejar, projetar e implementar essas funcionalidades, uma equipe é montada com especialistas em domínio, que trabalharão lado a lado com engenheiros de software. Na maioria das vezes, um especialista de domínio saberá quais contextos delimitados já existem e pelo que eles são responsáveis.

> **INFO:** De uma perspectiva DDD, é extremamente importante não tornar esses Bounded Contexts um limite técnico que é imposto aos especialistas em domínio.

Um Bounded Context irá expor um conjunto bem definido de APIs para que outras equipes de diferentes contextos delimitados possam consumir e interagir com a funcionalidade de Chamada de Propostas.

O Bounded Context Call for Proposals permitirá que uma equipe implemente todas as funcionalidades necessárias, independentemente das outras equipes. Essa equipe será responsável por todo o ciclo de vida do projeto, desde a concepção e implementação até a execução para o resto da empresa e para que seus(suas) clientes o consumam.

Assim que começa a projetar a funcionalidade Call for Proposals, você percebe que precisará consumir e interagir com outras equipes. Logo no início, os seguintes Bounded Contexts são identificados:

![Imagem 02_03: Bounded contexts da aplicação de conferências.](images/chapter_02_03.png)

Cada um desses Bounded Contexts deve pertencer a equipes diferentes, e precisamos ter certeza de que eles têm autonomia suficiente para progredir, criar novas versões com novos recursos e implantar componentes de software concretos para os ambientes de nossos(as) clientes.

Do lado prático, cada Bounded Context será implementado como um ou um conjunto de serviços que implementam os recursos do contexto. Não há necessidade de ser estrito quanto ao número de serviços que irão compor um Bounded Contexts, mas geralmente há um que se encarrega de expor um conjunto de APIs para o mundo exterior.

Para o propósito deste exemplo, um único serviço implementará toda a lógica do Bounded Contexts Call for Proposals, e a equipe por trás desse serviço será responsável por projetar suas APIs, escolher as estruturas que usarão e realmente implantá-las em um ambiente ao vivo.

Aprofundando os detalhes práticos, existem algumas das melhores práticas compartilhadas por muitas empresas e ferramentas:

- Um Repositório / Um Serviço + Entrega Contínua
- APIs abertas


#### Um Repositório / Um Serviço + Entrega Contínua

Normalmente, é recomendável manter todos os recursos técnicos necessários para executar um serviço próximos ao código-fonte. Isso facilita a manutenção e a carga cognitiva de compreensão de como executar nossos serviços em um ambiente real.

Hoje em dia, é bastante comum ter um serviço, digamos, escrito em Spring Boot versionado em um provedor git como o GitHub, onde podemos encontrar um Dockerfile para criar uma versão em container de nosso serviço, que pode ser executado em qualquer lugar onde um Docker daemon esteja presente.

Com o aumento da popularidade do Kubernetes, também é comum encontrar o Manifesto do Kubernetes (arquivos YAML) descrevendo como nosso serviço pode ser implantado em um cluster do Kubernetes. Finalmente, tendemos a usar pipelines de integração contínua para realmente construir todos esses componentes de software, portanto, é bastante comum também encontrar a definição de pipeline perto da fonte que precisa ser construída.

Você, como desenvolvedor(a) que visa ao Kubernetes como sua plataforma de implantação, agora é responsável por vários artefatos, não apenas pelo código-fonte do serviço Java.

![Imagem 02_04: Boas práticas em um processo deploy de aplicações no Kubernetes.](images/chapter_02_04.png)

Para fazer o deploy do seu código no Kubernetes: 

- Você precisará construir e testar seu código-fonte. Se for Java, você pode usar, por exemplo, Maven ou Gradle para fazer isso;

- Isso resultará em um arquivo JAR que você pode querer enviar para um repositório como Nexus ou Artifactory. Esse arquivo JAR já terá uma versão nele, e se você estiver usando Maven ou Gradle, esse JAR será identificado por seu GAV (Grupo/Artefato/Versão).

- A partir desse JAR, você cria uma imagem Docker definindo um Dockerfile que entende como executar sua aplicação Java com uma JVM fornecido.

- Por fim, se quiser fazer o deploy do seu container para um cluster Kubernetes, você precisará de alguns manifestos YAML.

- Opcionalmente, se você estiver criando muitos serviços, convém usar o Helm para empacotar e liberar esses arquivos YAML. Todos esses artefatos precisam ser versionados adequadamente, o que significa que, quando você constrói uma nova versão do seu arquivo JAR, um novo container precisa ser construído, e um novo gráfico do Helm precisa ser lançado.

  > **INFO:** O Helm fornece a ideia de gráficos (pacotes) que mapeiam um a um a forma como lidamos com nossos artefatos Maven. Se você estiver trabalhando com gráficos do Helm, esses gráficos geralmente também são enviados/liberados para um repositório de gráficos, como o Chart Museum.

Neste ponto, se você está pensando "isso é muito trabalho", você está 100% certo. Se você está pensando "eu não quero fazer tudo isso", você está absolutamente certo. Eu também não quero fazer isso. Se você quer que isso funcione, precisa usar ferramentas especializadas que já entregam todas essas funcionalidades de forma automatizada. Você deve tentar automatizar cada etapa, e a indústria usa pipelines de integração contínua para conseguir isso. Vamos falar sobre como entregar pipelines com Jenkins X.

> **INFO:** Neste exemplo, estamos visando ao Kubernetes e optamos por usar Jenkins X neste conjunto de ferramentas. Jenkins X traz CI/CD (Continuous Integration/Continuous Delivery) para o Kubernetes e faz parte da Continuous Delivery Foundation.

Como você pode notar, o Jenkins X não é apenas sobre integração contínua, mas também sobre entrega contínua. Ao cobrir a entrega contínua, o pipeline não para quando esses componentes são construídos. O pipeline é responsável por construir, testar e também implantar nossos artefatos em um ambiente ativo, onde serão executados para atender nossos(as) clientes. A parte “contínua” faz referência ao fato de que você deseja ter certeza de que a implantação de uma nova versão do seu serviço é fácil e que você terá como objetivo implantar novas versões em um curto período de tempo.

> **INFO:** Para alcançar a entrega contínua, o Jenkins X usa um conjunto de convenções para permitir que os desenvolvedores se concentrem na construção de valor de negócios. Essas convenções não são exclusivas do Jenkins X e fazem parte das melhores práticas coletadas de diferentes setores e profissionais. Projetos como o Jenkins X são os catalisadores para milhares de membros da comunidade, que são especialistas em CI/CD, o que resulta nas melhores práticas e ferramentas que as aplicam.

Uma das convenções usadas pelo Jenkins X é chamada “Trunk Based Development”. Basicamente, significa que cada alteração aplicada (merged) à branch master irá gerar uma nova versão de nossos artefatos. Na maioria das vezes, isso não é confortável para os desenvolvedores, já que muitas dessas práticas são comumente definidas em cada empresa e tendem a variar bastante. A principal motivação para usar algo como o Trunk Based Development é garantir que as equipes não gastem tempo definindo essas práticas. Ao trabalhar com essa convenção, você pode se concentrar na escrita do código e, quando o código estiver pronto e mesclado ao master, uma nova versão é criada e implantada em algum tipo de ambiente de teste para validações adicionais.

> **DICA:** Recomendo fortemente que, se você estiver iniciando um novo projeto, verifique as vantagens do Trunk Based Development, bem como do livro Accelerate, pois foi usado como base para a criação de ferramentas como [Jenkins X](https://jenkins-x.io/about/overview/accelerate/).

No fim das contas, Jenkins X usa ambas as convenções, “Um Repositório / Um Serviço” mais “Trunk Based Development”, para levar seu serviço do código-fonte para uma instância em execução dentro de um Cluster Kubernetes.

![Imagem 02_05: Com a prática proposta, um repositório representa um serviço que será implantado.](images/chapter_02_05.png)

Em nosso exemplo, os links a seguir demonstram todos esses conceitos em ação.

- [Pipeline](https://github.com/salaboy/fmtok8s-email/blob/master/jenkins-x.yml)
- [DockerFile](https://github.com/salaboy/fmtok8s-email/blob/master/Dockerfile)
- [Gráficos do Helm](https://github.com/salaboy/fmtok8s-email/tree/master/charts/fmtok8s-email)
- [Versões contínuas](https://github.com/salaboy/fmtok8s-email/releases)

Você pode encontrar o mesmo setup para todos os projetos dentro da Demonstração do Site de Conferência.


#### APIs abertas
Se você já está implementando um Bounded Context, logo no início você precisará projetar e especificar que tipo de interface irá expor para outro Bounded Context e serviços terceiros que possam estar interessados ​​na funcionalidade que seu contexto fornece. Uma maneira popular de implementar essas APIs são os terminais REST.

> **INFO:** Como você provavelmente está familiarizado com endpoints REST, esta seção se concentra na [Especificação de API aberta](https://github.com/OAI/OpenAPI-Specification)

Conforme definido no texto das especificações *“a especificação OpenAPI remove as suposições ao chamar um serviço.”* Hoje em dia, frameworks populares como Spring Boot vêm com integração pronta para uso com API aberta e ferramentas de API aberta.

Apenas adicionando uma extensão/iniciador Spring Boot, você permite que sua aplicação exponha uma interface de usuário que serve como documentação e navegador ao vivo de suas APIs.

Se você está usando Spring Boot padrão (Tomcat), será preciso adicionar ao seu arquivo `pom.xml`:

```xml
<dependency>
   <groupId>org.springdoc</groupId>
   <artifactId>springdoc-openapi-ui</artifactId>
   <version>${springdoc-openapi-ui.version}</version>
</dependency>
```
Se você estiver usando o Webflux, a pilha reativa que você precisa adicionar é:

```xml
<dependency>
   <groupId>org.springdoc</groupId>
   <artifactId>springdoc-openapi-webflux-ui</artifactId>
   <version>${springdoc-openapi-ui.version}</version>
</dependency>
```

- <https://github.com/salaboy/fmtok8s-email/blob/master/pom.xml#L40>

Em projetos da vida real, essas interfaces de usuário e documentos de especificação de API podem ser usados por outras equipes para entender com detalhes concretos como interagir com seus serviços. Quanto mais cedo você expõe uma API, mais cedo outras equipes podem começar a aproveitar seu serviço.

A captura de tela a seguir mostra a interface de usuário da API aberta, a qual é fornecida apenas incluindo a dependência anterior. Essa tela pode ser acessada apontando seu navegador para *host:port/swagger-ui.html*. Ela fornece um cliente simples para interagir com seus serviços, entender quais terminais estão expostos e quais dados esses terminais esperam e retornam.

![Imagem 02_06: Interface da Swagger API](images/chapter_02_06.png)

Sinta-se à vontade para clonar um dos serviços deste exemplo e executá-lo com o comando `mvn spring-boot: run` para explorar as definições de APIs de cada serviço. Por padrão, cada serviço iniciará na porta 8080, portanto, você deve apontar seu navegador para <http://localhost:8080/swagger-ui.html>

### Mapa de contexto para entender as interações técnicas e da equipe

Bounded Contexts são ótimos para entender um conjunto bem definido de funcionalidades que precisam ser fornecidas juntas. Quando temos vários desses contextos, precisamos entender como eles irão interagir uns com os outros e seus relacionamentos. É aí que o conceito de Mapas de Contexto realmente ajuda. Com mapas de contexto, você pode mapear as relações entre o bounded contexts e o que eles realmente precisam para interagir. O mapeamento de contexto também fornece visibilidade sobre como as equipes responsáveis ​​por cada contexto limitado irão interagir com outras equipes.

Do lado prático, trata-se de integrações de sistema. Como nossos serviços ou os serviços que expõem algum tipo de API conversam entre si. Como eles transformam e movem dados e como eles sabem quais serviços estão disponíveis para consumo.

Como você pode imaginar, as APIs são extremamente importantes, mas entender quem vai consumir nossas APIs, o que se espera dessas APIs e quem realmente depende de nós é igualmente crítico.

Mapas de contexto bem definidos ajudam muito a planejar e compreender como esses bounded contexts “isolados” e as equipes que trabalham neles irão interagir no dia a dia.

Para nosso exemplo, o seguinte mapa de contexto faria sentido:
![Imagem 02_07: Context Map para o Call for Papers da aplicação de conferências](images/chapter_02_07.png)

Esse diagrama descreve as relações entre o bounded context simples que temos para nossa aplicação do site de conferência. Aqui podemos ver que existe uma relação **Cliente/Fornecedor** entre a Call for Proposals e o bounded context da Agenda da Conferência. A Call for Proposals **é um consumidor** da Agenda da Conferência do serviço upstream. Entre essas duas equipes, existe também uma relação de **Parceria(Partnership)**, pois elas precisam colaborar para fazer as coisas. Isso significa que a comunicação entre essas duas equipes é importante e que elas devem ser capazes de influenciar o roteiro uma da outra.

Por outro lado, a relação com o serviço de Notificação é diferente. A Call for Proposals tem uma relação ascendente com o Bounded Context de Notificação (Notification), mas vai **confortar** com seus contratos. Isso significa que, da perspectiva da equipe de Call for Proposals, eles não podem influenciar ou alterar as APIs do Bounded Context de notificação. Isso acontece muito quando temos sistemas legados ou quando esse bounded context é externo à nossa empresa.

> **DICA:** Pulando para o lado prático, embora as integrações de sistema sejam um tópico muito amplo, esta seção se concentra em uma recomendação muito prática: “Você deve aprender sobre testes de contrato orientados ao consumidor (Consumer-Driven Contract)”. Mais uma vez, Martin Fowler tem um artigo, publicado em 2006, sobre isso: <https://martinfowler.com/articles/consumerDrivenContracts.html>.

Embora o tópico em si não seja novo, existem ferramentas muito atualizadas para realmente implementar isso em seus projetos, como [Spring Cloud Contracts](https://spring.io/projects/spring-cloud-contract).

#### Bônus: Implementação de contratos com Spring Cloud Contracts

Com Spring Cloud Contracts, a história é assim: primeiro você define um contrato para suas APIs. Isso basicamente significa que tipo de solicitação o consumidor deve enviar e que tipo de resposta precisamos fornecer como serviço.

Um contrato é semelhante a isto: <https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/test/resources/contracts/shouldAcceptPostProposal.groovy>

```groovy
Contract.make {
       name "should accept POST with new Proposal"
       request{
           method 'POST'
           url '/'
           body([
               "title": $(anyNonEmptyString()),
               "description": $(anyNonEmptyString()),
               "author": $(anyNonEmptyString()),
               "email": $(anyEmail())
           ])
           headers {
               contentType('application/json')
           }
       }
       response {
           status OK()
           headers {
               contentType('application/json')
           }
           body(
                   "id": $(anyUuid()),
                   "title": $(anyNonEmptyString()),
                   "description": $(anyNonEmptyString()),
                   "author": $(anyNonEmptyString()),
                   "email": $(anyEmail())
           )
       }
   }
```
Este contrato define a interação para o envio de uma nova Proposta ao Serviço Call for Proposal. Como você pode ver, ele envolve uma solicitação `POST` e um body com algumas propriedades predefinidas, incluindo um `header` com um `Content Type` muito específico. Esse contrato também define que o retorno para o consumidor agregará às informações enviadas uma propriedade `id` com formato `UUID`.

Agora, esse contrato pode ser usado para gerar um teste para realmente verificar se o seu serviço está funcionando conforme o esperado, do ponto de vista do consumidor. Portanto, se você definiu qualquer contrato em seu projeto ao construir e testar esse projeto, os contratos serão executados em uma instância real de seu serviço. Isso nos permite ter certeza de que quebraremos a construção se um contrato for quebrado. Para criar e executar automaticamente esses testes, você só precisa adicionar uma dependência e um plug-in ao seu projeto maven:
<https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/pom.xml#L50>

```xml
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-contract-verifier</artifactId>
  <version>${spring.cloud.contract}</version>
  <scope>test</scope>
</dependency>

```

E na seção `<build><plugins>`, o seguinte plugin:
<https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/pom.xml#L88>

```xml
<plugin>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-contract-maven-plugin</artifactId>
  <version>${spring.cloud.contract}</version>
  <extensions>true</extensions>
  <configuration>
    <packageWithBaseClasses>com.salaboy.conferences.c4p</packageWithBaseClasses>
    <testMode>EXPLICIT</testMode>
   </configuration>
</plugin>

```

Finalmente, dependendo da forma do serviço que você testará, pode ser necessária alguma confirmação: <https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/test/java/com/salaboy/conferences/c4p/ContractVerifierBase.java#L16>

```java
@RunWith(SpringRunner.class)
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        properties = "server.port=0")
public abstract class ContractVerifierBase {

    @LocalServerPort
    int port;
    
    ...
}
```

Se você precisar adicionar qualquer código de inicialização, esta é a classe que todo teste gerado automaticamente deve herdar.

Agora que o contrato foi validado com cada build, também podemos usar o contrato para gerar um Stub, que é um serviço que se comporta como o serviço real, mas com dados fictícios. Os dados fictícios também são gerados automaticamente, pois também podem ser fornecidos pela definição do contrato. Esse stub é um artefato por si só que pode ser distribuído para outros serviços, por exemplo, aqueles que consomem o serviço “real” para teste.

Isso basicamente significa que agora, toda vez que você construir seu serviço, **dois** arquivos JAR serão criados. Um é o JAR da aplicação Spring Boot real, e o outro é o Stub de Serviço.
Esse stub de serviço pode ser enviado automaticamente para o repositório do seu artefato (por exemplo, Nexus ou Artifactory) e viverá no mesmo grupo e nome do artefato da sua aplicação JAR.

Finalmente, um Serviço X projetado para consumir seu serviço pode criar testes que iniciarão o stub gerado antes, localmente, para evitar a necessidade de uma instância real ou uma configuração de ambiente inteira. Você pode facilmente iniciar o stub antes de seus testes usando as seguintes anotações:


```java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.REMOTE, repositoryRoot = "<your nexus repository>", ids = "com.salaboy.conferences:fmtok8s-c4p")
public class C4PApisTests {
   @StubRunnerPort("fmtok8s-c4p")
    int producerPort;

...
}
```

<https://github.com/salaboy/fmtok8s-api-gateway/blob/master/src/test/java/com/salaboy/conferences/site/C4PApisTests.java#L29>

Isso baixa automaticamente a versão mais recente do stub e o executa antes que seu teste comece, usando uma porta aleatória que você pode obter via injection, usando `@StubRunnerPort`. 

É importante observar que tanto o Serviço quanto os contratos são versionados juntos, como parte da mesma base de código. Isso implica que o Stub gerado e o próprio Serviço terão a mesma versão. Um serviço de consumidor, para executar seus testes, pode depender do Stub, pois nunca deve depender do próprio serviço. Assim que o consumidor tiver testado por meio do Stub de Serviço do produtor, você pode reconhecer rapidamente quando um contrato é quebrado ou quando uma nova versão do contrato não é mais compatível com os consumidores, pois os testes usando os Stubs serão interrompidos quando versões novas e incompatíveis forem lançadas. Neste momento, os consumidores se deparam com uma decisão simples: ficar dependendo dos contratos antigos com uma versão fixa, ou atualizar para a versão mais recente do contrato. Isso pode exigir que você execute várias versões do seu serviço ao mesmo tempo. Felizmente para nós, o Kubernetes foi criado para oferecer suporte a esses cenários. Você pode ler sobre versões canário (Canary Releases) se estiver interessado em aspectos de multi-version deployments.

> **DICA:** O capítulo [Cloud](#chapter_08) cobre Canary Releases, bem como outras estratégias de implantação.

Ambos, bounded contexts e mapas de contexto, são ótimas ferramentas conceituais para entender como estruturar suas equipes e seu software, mas, mais importante, esses conceitos ajudam você a se concentrar no valor do negócio.


### Foco no valor do negócio

Ao trabalhar com aplicativos reais, você precisa se concentrar no que vai agregar mais valor e resolver mais problemas para o seu negócio. Sendo muito prático, tendo a analisar casos de uso e como o bounded context, em conjunto com os mapas de contexto, fornece um fluxo de negócios de ponta a ponta.

Para este exemplo específico, o cenário de conferência, estamos analisando o fluxo básico da Call for Proposals, que se parece com isto:

1. O palestrante em potencial envia uma proposta pelo site da conferência
2. O Conselho/Comitê analisa a proposta
3. Se a proposta for aceita
   1. Um novo item da agenda é criado e publicado na página da agenda
4. Uma notificação é enviada por e-mail para propostas aceitas e rejeitadas

Você precisa prestar atenção às suas interações humanas, pois essas interações tendem a exigir comportamentos assíncronos, como lembretes, notificações, alertas, bem como interfaces de usuário, que devem ser cuidadosamente projetadas.

> **DICA:** Como engenheiros, tendemos a simplificar e subestimar a quantidade de trabalho e iterações que podem exigir a criação de uma boa experiência do(da) usuário(a).

A interface de usuário que cobre esse cenário simples é assim:

* A página principal dentro do site da conferência exibe a agenda dividida por dias. Os itens da pauta são os que já estão confirmados e foram aprovados pelo comitê.

  ![Imagem 02_08: Página Principal do site da aplicação de conferências com lista de itens confirmados e aprovados.](images/chapter_02_08.png)

  {pagebreak}

* A página principal também permite que palestrantes em potencial enviem propostas, preenchendo um formulário:

  ![Imagem 02_09: Formulário de Call for Papers](images/chapter_02_09.png)

  {pagebreak}

* Assim que a proposta for enviada, o palestrante em potencial precisará aguardar a aprovação ou rejeição do comitê. Os membros do comitê têm uma página de back-office, onde podem aprovar ou rejeitar cada proposta enviada:

  ![Imagem 02_10: Página de aprovação acessada pelo comitê do evento](images/chapter_02_10.png)

  {pagebreak}

* A página de back-office também oferece aos membros do comitê a opção de enviar notificações por e-mail aos palestrantes em potencial.

  ![Imagem 02_10: Página de utilizada pela organização para contactar potenciais palestrantes.](images/chapter_02_11.png)



Mais uma vez, você pode notar a simplificação proposital desse cenário, para estabelecer um conjunto básico de funcionalidades, iterar rapidamente e fazê-lo funcionar e então expandir os requisitos.

{pagebreak}

#### Arquitetura e Serviços

De uma perspectiva arquitetônica, parece mais assim:

![Imagem 02_12: Arquitetura e usuários da aplicação de conferências.](images/chapter_01_12.png)

A Interface do Usuário com alguma capacidade de roteamento é necessária para encaminhar solicitações ao Serviço Call for Proposals (C4P) para o Serviço de Agenda ou Emails. Neste exemplo, todas as comunicações acontecem por meio de invocações HTTP/Rest.

### Gateway de API / Interface do usuário
Na maioria das vezes, um Gateway de API também é usado para ocultar acesso direto a todos os outros serviços. É bastante comum ver esse serviço delegando autorização e autenticação a um provedor OAuth ou SAML, servindo como uma barreira de segurança para o mundo externo. O exemplo usa o [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway), que fornece o mecanismo de roteamento para encaminhar solicitações de entrada para o restante dos serviços. O Spring Cloud Gateway nos permite transformar qualquer aplicação Spring Boot em um roteador de solicitação com recursos avançados.

> **INFO:** É importante observar que o Spring Cloud Gateway oferece a flexibilidade de realmente adicionar programaticamente qualquer transformação que você deseja/precisa nas solicitações (requests) de entrada. Esse poder e liberdade vêm com a desvantagem de que depende de você manter, testar e corrigir o bug. Em grandes projetos, você pode querer avaliar um gateway de API de terceiros (como Kong, 3Scale, Apigee etc.) com base nos requisitos do seu projeto.

O módulo API Gateway / interface do usuário pode ser encontrado neste repositório: <https://github.com/salaboy/fmtok8s-api-gateway>

O que adiciona às suas dependências maven:

```xml
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
```
E configure as rotas padrão para nossos serviços dentro do arquivo application.yaml:
<https://github.com/salaboy/fmtok8s-api-gateway/blob/master/src/main/resources/application.yaml#L4>

{language=yaml}

```yaml
spring:
  cloud:
    gateway:
      routes:
      - id: c4p
        uri: ${C4P_SERVICE:http://fmtok8s-c4p}
        predicates:
        - Path=/c4p/**
        filters:
          - RewritePath=/c4p/(?<id>.*), /$\{id}
      - id: email
        uri: ${EMAIL_SERVICE:http://fmtok8s-email}
        predicates:
        - Path=/email/**
        filters:
          - RewritePath=/email/(?<id>.*), /$\{id}
```

Essas rotas definem um caminho para o gateway como `/c4p/**`, que irá encaminhar automaticamente a solicitação para o serviço <http://fmtok8s-c4p>.

> **INFO:** A interface de usuário do site pode ser encontrada aqui: <https://github.com/salaboy/fmtok8s-api-gateway/tree/master/src/main/resources/templates>
> O Controller que busca os dados dos serviços de back-end, aqui: <https://github.com/salaboy/fmtok8s-api-gateway/blob/master/src/main/java/com/salaboy/conferences/site/DemoApplication.java>

Como estamos executando no Kubernetes, podemos usar o nome do serviço Kubernetes em vez de apontar para um pod específico. Esse mecanismo de roteamento nos permite expor apenas os Endpoints do Gateway de API para o mundo externo, deixando todos os outros serviços atrás de uma rede segura.

### Eventos de domínio e o serviço Call for Proposals
Como o fluxo em análise é fundamental para o Bounded Context Call For Proposals, não é surpresa que a lógica central pertença ao Serviço Call For Proposals, mais concretamente, às duas funções a seguir: Envio de Proposta e Decisão Tomada pelo Conselho (Proposal Submission and Decision Made By the Board).

> **INFO:** O Serviço Call for Proposals pode ser encontrado aqui: <https://github.com/salaboy/fmtok8s-c4p/>

O endpoint de Envio de Proposta aceita uma proposta da interface do usuário e a armazena em um banco de dados ou storage. Este é um passo importante, precisamos garantir que não perderemos propostas. Observe que podemos estar interessados ​​em emitir um evento de domínio DDD neste momento, pois outros sistemas/aplicações podem estar interessados ​​em reagir sempre que uma proposta for recebida.

> **INFO:** Verifique uma implementação real aqui: <https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/main/java/com/salaboy/conferences/c4p/C4PController.java#L37>

Mais importante, o endpoint Decision Made by the Board registra uma decisão feita pela diretoria, mas também define as etapas a seguir com base nessa decisão. Na vida real, essa decisão afetará o curso da ação. Na maioria das vezes, esses pontos de decisão e as ações derivadas deles são essenciais para administrar um negócio eficiente e com boa relação custo-benefício.
<https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/main/java/com/salaboy/conferences/c4p/C4PController.java#L60>

```java
@PostMapping(value = "/{id}/decision")
    public void decide(@PathVariable("id") String id, @RequestBody ProposalDecision decision) {
        emitEvent("> Proposal Approved Event ( " + ((decision.isApproved()) ? "Approved" : "Rejected") + ")");
        Optional<Proposal> proposalOptional = proposalStorageService.getProposalById(id);
        if (proposalOptional.isPresent()) {
            Proposal proposal = proposalOptional.get();

            // Apply Decision to Proposal
            proposal.setApproved(decision.isApproved());
            proposal.setStatus(ProposalStatus.DECIDED);
            proposalStorageService.add(proposal);

//          Only if it is Approved create a new Agenda Item into the Agenda Service
            if (decision.isApproved()) {
                agendaService.createAgendaItem(proposal);
            }

            // Notify Potential Speaker By Email
            emailService.notifySpeakerByEmail(decision, proposal);
        } else {
            emitEvent(" Proposal Not Found Event (" + id + ")");
        }

    }
```

Como você provavelmente pode imaginar, esse método contém a lógica de todo o fluxo. Começa por verificar se o `Id` da proposta pode ser encontrado; se estiver presente, aplicará a decisão ao objeto da proposta (`Approved` ou `Rejected`); e, somente se for aprovado, chame o Serviço Agenda para criar um novo item da agenda. Não importa se foi aceito ou não, um e-mail deve ser enviado ao potencial palestrante para notificá-lo sobre a decisão. Ambos [agendaService](https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/main/java/com/salaboy/conferences/c4p/services/AgendaService.java) e [emailService](https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/main/java/com/salaboy/conferences/c4p/services/EmailService.java) estão simplesmente encapsulando chamadas REST simples.

Este exemplo de método simples, em cenários da vida real, nunca é simples. Devido à complexidade inerente aos desafios da vida real, você deve esperar que métodos simples como o discutido acima se tornem monstros reais. A próxima seção aborda algumas das principais armadilhas e considerações que você deve ter em mente ao escrever a lógica de negócios que é a chave para seu domínio. A próxima seção também tenta compartilhar recursos, abordagens e projetos valiosos que podem ajudar você a tomar melhores decisões no início para evitar erros comuns.

### Armadilhas comuns
As aplicações da vida real são complexas, e essa complexidade tende a vir da complexidade inerente aos problemas que estamos tentando resolver. Podemos tentar reduzir essa complexidade usando uma abordagem de melhoria contínua e certificando-nos de que não estamos reinventando rodas desnecessárias, que não estão fornecendo qualquer diferenciação nos negócios.

Vamos começar com algo que você pode ter enfrentado no passado: comunicações REST para REST podem ser desafiadoras.

#### Comunicações REST para REST podem ser desafiadoras
Se você olhar o exemplo fornecido na seção anterior, os serviços de Agenda e de Email são chamados a partir do Serviço Call for Proposals usando uma [chamada REST](https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/main/java/com/salaboy/conferences/c4p/services/AgendaService.java#L29). Como discutimos antes, essas interações representam uma parte fundamental de nosso fluxo de negócios, então você precisa ter certeza de que essas interações ocorram conforme planejado 100% das vezes. É vital que nosso aplicativo não acabe em um estado inconsistente - por exemplo, uma proposta é aprovada e publicada na agenda, mas nunca enviamos uma notificação ao palestrante. Como está codificado neste exemplo, se os Serviços de Agenda ou de Email ficarem inativos, a solicitação HTTP silenciosamente falhará.

Este requisito básico, ao trabalhar com sistemas distribuídos onde não existe estado compartilhado entre os serviços, torna-se um desafio com várias soluções possíveis.

Uma solução comum é escrever dentro de nosso código de serviço para tentar novamente em caso de falha, o que torna cada chamada muito mais complicada. No passado, havia várias bibliotecas que forneciam auxiliares para tais situações. É importante notar que o Kubernetes, por padrão, não lida com esse tipo de falha de forma alguma.

Outra solução pode ser usar um mecanismo de mensagem ou pub/sub para comunicar nossos serviços que, prontos para uso, oferecem mais garantias sobre a entrega das mensagens enviadas. Mudar para o uso de mensagens como RabbitMQ ou Kafka apresenta outro conjunto de desafios, na maioria das vezes relacionado a lidar com outra peça complexa de infraestrutura que precisa ser mantida ao longo do tempo. No entanto, o sistema de mensagens provou ser robusto e a única opção para determinados cenários em que essas garantias são necessárias e o volume de interações é alto.

Finalmente, uma abordagem mais recente são as Service Meshes, em que delegamos a responsabilidade de tentar novamente, por exemplo, à infraestrutura. O Service Mesh usa proxies para inspecionar cargas HTTP e códigos de erro, de forma que novas tentativas automáticas possam ser feitas em caso de falha.

> **DICA:** Você deve dar uma olhada em Istio, Gloo e LinkerD se quiser entender mais sobre como funcionam Service Meshes e quais são suas vantagens. Mais detalhes sobre as Service Meshes são compartilhados no capítulo [Cloud](#chapter_08).

#### Fluxo enterrado no código
É bastante comum encontrar lógicas de negócios complexas escondidas dentro de nossos serviços, de certa forma obscurecidas por todos os padrões necessários para lidar com erros técnicos, buscar dados de diferentes fontes e transformar dados em diferentes formatos. Em projetos da vida real, fica muito difícil para os especialistas em Domínio entenderem de fato o código que implementa seus fluxos de negócios.

O exemplo discutido neste capítulo se tornaria difícil de ler se adicionássemos o código para lidar com outros aspectos, como:

- Caminhos infelizes e casos excepcionais: como o palestrante que apresentou a proposta desapareceu e não está respondendo a e-mails.
- Eventos, lembretes e restrições com base no tempo (Time-Based): por exemplo, programe um lembrete para os membros do conselho analisarem uma proposta antes de três dias após o envio. Cancele o lembrete se a decisão foi tomada antes do prazo.
- Novos requisitos são adicionados, o que leva as pessoas desenvolvedoras a mudar a sequência do fluxo, como, por exemplo, um e-mail que precisa ser enviado com um link de confirmação para o palestrante antes de publicarmos e aprovarmos a palestra para a agenda. Quanto mais requisitos, mais código precisamos adicionar, e mais ele vira um código espaguete.
- Relatórios e análises: por exemplo, seu gerente quer saber quantas propostas recebemos por dia e quanto tempo em média leva para aprovar ou rejeitar propostas. Você pode ficar tentado a adicionar novos endpoints para lidar com esses relatórios dentro do mesmo serviço.

Não existem soluções milagrosas para enfrentar esses desafios, mas eu gostaria de mencionar algumas coisas que podem, em seus cenários, ajudar a reduzir a complexidade e fornecer visibilidade sobre como seus serviços estão funcionando.

Os eventos de domínio (Domain Events) são introduzidos no DDD para externalizar o estado da aplicação que pode ser relevante para o consumo de outros serviços. Do lado prático, esses eventos podem ser criados usando [Cloud Events](https://cloudevents.io/), que fornece um formato independente de transporte para a troca de eventos.

As ferramentas de Orquestração de Serviço podem ser usadas em conjunto com Cloud Events para externalizar a lógica de negócios enterrada em nossos serviços.

> **DICA:** Verifique projetos como Zeebe, jBPM ou Kogito para entender mais sobre como essas ferramentas podem ajudar você. Além disso, você pode achar este post sobre Cloud Events muito útil.

Finalmente, conforme discutido no livro *Implementing DDD*, patterns (padrões) como CQRS (Command/Query Responsibility Segregation [Segregação de Responsabilidade de Consulta e Comando]) podem ajudá-lo muito ao lidar com relatórios e análises. Você quer evitar a execução de relatórios caros ou rotinas intensas de processamento de dados no banco de dados de serviço. Ao aplicar o CQRS, você externaliza os dados que está interessado em relatar em um armazenamento separado que possui um formato otimizado para indexar, pesquisar e resumir dados. Uma abordagem popular é enviar dados para ElasticSearch para a indexação completa do texto. Então, na sua aplicação, se você deseja pesquisar entre milhares de propostas, você não consulta o Serviço Call for Proposals. Em vez disso, você usa os índices ElasticSearch, descarregando o serviço Call for Proposals, para que ele possa continuar aceitando propostas para suas conferências.

#### Adaptadores (Adapters) para sistemas legados

Uma breve nota sobre Sistemas Legados: tente abstraí-los para que você tenha controle sobre suas APIs. Para o exemplo visto neste capítulo, um serviço chamado Email foi introduzido para expor via endpoints HTTP a funcionalidade de um servidor de email. Isso foi feito propositalmente para destacar as vantagens de fornecer um adaptador para um servidor que não podemos alterar (um servidor SMTP). Nesse adaptador, podemos incluir funções auxiliares, modelos e funcionalidades específicas de domínio que são exigidas por nossos casos de uso.

O serviço Email fornecido não inclui uma conexão SMTP, mas expõe um conjunto de APIs que são fáceis de consumir e não exigem que outros serviços incluam clientes SMTP.

> **INFO:** O código-fonte desse serviço pode ser encontrado aqui: <https://github.com/salaboy/fmtok8s-email>

Considere a criação de adapters para seus serviços legados. Lembre-se de que, dentro do Kubernetes, mesmo que os adapters sejam criados em containers separados, esses containers podem ser executados dentro do mesmo host, evitando um salto (hop) extra de rede.

## Resumindo

Este capítulo abordou uma ampla gama de ferramentas e princípios referentes a uma aplicação de exemplo. Algumas dessas ferramentas farão sentido para o seu cenário, outras não. O que é importante tirar daqui pode ser resumido nos seguintes pontos:

- Otimize as decisões sobre a construção ou integração de software de terceiros para resolver questões específicas ou cruzadas (cross-cutting concerns). Para ganhar agilidade, é fundamental ter um processo claro para avaliar as ferramentas em comparação com a construção de software internamente para desafios que não são essenciais para o seu negócio.
- Mantenha suas equipes atualizadas com treinamentos. A transferência de conhecimento é um grande problema quando o stack tecnológico é amplo e complexo. Aprenda a identificar quais são os principais tópicos com os quais suas equipes têm dificuldade e encontre treinamento que possa ajudar a difundir o conhecimento entre elas.
- Use convenções em vez de definições internas, aproveite as comunidades de código aberto, que são ótimos lugares para encontrar as melhores práticas aplicadas, inovações e tendências. Não tenha medo de participar, envolver-se e compartilhar seus aprendizados.
- Considere o uso de provedores SaaS (Software as a Service) em vez de hospedagem interna, quando possível. Se você já está rodando em um provedor na nuvem, precisa seriamente considerar o conjunto de serviços que eles oferecem. Provedores na nuvem e ofertas de SaaS economizarão um tempo valioso ao configurar e manter peças-chave de sua infraestrutura. Mesmo um(a) desenvolvedor(a) podendo executar Kafka, ElasticSearch ou qualquer outra ferramenta de terceiros usando containers, não significa que ele(ela) esteja disposto a manter, atualizar e fazer backup desses serviços para toda a empresa.

================================================
FILE: manuscript/chapter_03.md
================================================
# Clean code {#chapter_03}

São claros e reconhecidos os grandes benefícios obtidos por meio da utilização de boas práticas no código. Algo que logo se nota ao se trabalhar com um código limpo e fluido é a melhor legibilidade de código e a maior facilidade de manutenção. Mas, ao construir aplicações e discutir práticas de arquitetura, há um outro ponto que não podemos deixar de lado: a integridade dos dados que serão manipulados. Será que as boas práticas de código refletem de forma positiva na integridade desses dados? Um dos tópicos primordiais tópicos cobertos pelo livro [Clean Code](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship-ebook/dp/B001GSTOAM) é que, diferentemente da programação estruturada, a orientação a objetos expõe comportamento escondendo os dados. Com isso em mente, neste capítulo discorreremos sobre os benefícios da utilização de boas práticas de código e as vantagens obtidas ao se implementarem modelos ricos.

## Modelos Ricos

Levando em consideração uma aplicação que adota práticas de clean code, os ganhos de performance são consideráveis, uma vez que, por não trafegar e validar dados em bancos de dados, há economia tanto de processamento de rede quanto de hardware. Durante o desenvolvimento, há vários desafios em se "blindar" o código, como os inúmeros conceitos técnicos que são aplicados e a junção de negócio com o interesse na criação de uma linguagem ubíqua. Que tal aprofundar um pouco mais nesse conceito através da criação de um exemplo? 

Vamos criar uma aplicação de gestão de jogadores de futebol. Teremos o conceito de `time`, e dentro de um `time` teremos as seguintes informações:

- O nome do jogador, representado pelo atributo `name` da classe `Player`;
- A posição (`position` da classe `Player`) do jogador (goleiro, ataque, defesa e meio de campo); 
- O ano em que o jogador entrou no time, representado pelo atributo `start` da classe `Player`; 
- O ano em que o jogador saiu do time, representado pelo atributo `end` da classe `Player`; 
- O número de gols que o jogador realizou no time, representado pelo atributo `goals` da classe `Player`;
- O salário do jogador, representado pelo atributo `salary` da classe `Player`;
- O email para contato, no atributo `email`;
- A relação com o time, representada pela classe `Team`;	
- Lembrando a regra, _um time não deve ter mais que vinte membros_.

Com base nas informações citadas, a primeira versão do modelo é mostrada a seguir:

```java
import java.math.BigDecimal;

public class Player {

    String name;

    Integer start;

    Integer end;

    String email;

    String position;

    Integer goals;

    BigDecimal salary;
}

public class Team {

    String name;

    List<Player> players;
}

```

À primeira vista, podemos notar possíveis melhorias no código. Note que o atributo `position` é do tipo `String`, o que não faz sentido, uma vez que as posições de jogadores serão sempre as mesmas: goleiro (goalkeeper), defesa (defender), meio de campo (midfielder) e atacante (forward). Podemos melhorar esse cenário aplicando o conceito de [Value Objects](https://martinfowler.com/bliki/ValueObject.html) através da utilização de um `Enum`:

```java
public enum Position {
    GOALKEEPER, DEFENDER, MIDFIELDER, FORWARD;
}
```

Uma vez que definimos o modelo inicial, nosso próximo passo é analisar segurança e encapsulamento dos objetos. Esta é umas das bases para um bom código orientado a objetos: a possibilidade de "esconder" os dados e expor apenas os comportamentos. A criação dos métodos assessores (setter) deve ser considerado como último recurso para acesso ao objeto. Um outro ponto é: deve-se avaliar a necessidade de esses métodos serem públicos, ou seja, considere criá-los como `default` ou `protected`, caso seja possível. 

Sob essa perspectiva, vamos analisar algumas regras de negócio da nossa aplicação de exemplo: 

- Os jogadores não mudam de e-mail, nome e de posição, e só é possível marcar um gol por vez. Repare que a criação de métodos setters não é importante para esses atributos. 
- O ano de saída pode estar vazio, porém, quando preenchido, deverá ser posterior à data de entrada; 
- Apenas o time (representado pela classe `Team`) é responsável por gerenciar os jogadores (`Player`), ou seja, será necessário criar métodos para adicionar jogadores ao time. 

Vamos trabalhar na classe `Team`. Teremos de criar um método para adicionar vários players (`Player`) no `Team`. Também precisaremos de um método (get) que retorne os players. É importante validar a entrada de players, uma vez que não faz sentido adicionar um player nulo. Devemos também garantir que apenas a classe `Team` adicione/remova players. Para isso, devemos assegurar que essa classe retorne uma lista apenas de leitura, do contrário teremos problemas com encapsulamento. Uma maneira de resolver isso seria retornar uma lista como no exemplo a seguir:

```java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class Team {

    private static final int SIZE = 20;

    private String name;

    private List<Player> players = new ArrayList<>();

    @Deprecated
    Team() {
    }

    private Team(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void add(Player player) {
        Objects.requireNonNull(player, "player is required");

        if (players.size() == SIZE) {
            throw new IllegalArgumentException("The team is full");
        }
        this.players.add(player);
    }

    public List<Player> getPlayers() {
        return Collections.unmodifiableList(players);
    }

    public static Team of(String name) {
        return new Team(Objects.requireNonNull(name, "name is required"));
    }
}

```

> **INFO**: Muitos frameworks precisam que o construtor padrão exista por questão de realizar a criação de uma instância a partir da API de Reflection. Como o objetivo é desencorajar o uso do construtor padrão ao invés do uso do método de construção, o construtor será anotado com [Deprecated](https://www.baeldung.com/java-deprecated). A anotação Deprecated indica que esse método não deve ser utilizado.

Com relação à classe `Player`, todos os atributos terão getters padrão, com exceção do atributo `end`, que terá um tratamento especial: o `getEnd` retornará um `Optional`, uma vez que o `end`pode ser nulo. Outro ponto é o método `setEnd`, que só será íntegro caso o último ano seja igual ou maior que o ano de início do player, ou seja, se ele começou a jogar em 2004, não faz sentido ele ter terminado de jogar em 2002. Desse modo, o setter terá de fazer a validação no momento do acesso.

```java
import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;

public class Player {

    private String name;

    private Integer start;

    private Integer end;

    private String email;

    private Position position;

    private BigDecimal salary;

    private int goal = 0;


    public String getName() {
        return name;
    }

    public Integer getStart() {
        return start;
    }

    public String getEmail() {
        return email;
    }

    public Position getPosition() {
        return position;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public Optional<Integer> getEnd() {
        return Optional.ofNullable(end);
    }

    public void setEnd(Integer end) {
        if (end != null && end <= start) {
            throw new IllegalArgumentException("the last year of a player must be equal or higher than the start.");
        }
        this.end = end;
    }
}

    public int getGoal() {
        return goal;
    }

public void goal() {
       goal++;
}

```

Uma vez definidos os métodos de acesso, o próximo passo está na criação das instâncias de `Team` e `Player`. Como boa parte das informações é obrigatória para se criar uma instância válida, o primeiro movimento natural seria a criação de um método construtor. Isso é válido com objetos simples, como o `Team`, porém a class `Player` tem mais complexidades, como:

- A quantidade de parâmetros: pode gerar um construtor [polyadic](https://medium.com/coding-skills/clean-code-101-meaningful-names-and-functions-bf450456d90c) (construtor com mais de três argumentos).
- A complexidade das validações: não faz sentido um player começar a jogar antes de 1863, uma vez que o esporte nasceu nesse ano.

Para resolver esses problemas, executaremos dois passos: utilização de tipos customizados e aplicação do padrão Builder.

### Tipos customizados

A primeira estratégia é a criação de um tipo. Essa estratégia faz sentido quando um objeto tem grande complexidade, como por exemplo objetos que lidam com dinheiro e data. Trazer essa complexidade para a entidade pode quebrar o princípio da responsabilidade única. Existe um artigo muito bom escrito por Martin Fowler, [When to Make a Type](https://martinfowler.com/ieeeSoftware/whenType.pdf), que explica as vantagens de tais recursos. Também não queremos reinventar a roda, portanto, para representar ano e dinheiro utilizaremos as APIs de Date/Time que nasceram do Java 8 e a Money-API. O único tipo que precisaremos criar é o tipo `e-mail`, como mostra o código a seguir:

```java
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Pattern;

public final class Email implements Supplier<String> {

    private static final String EMAIL_PATTERN =
            "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
                    + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

    private static final Pattern PATTERN = Pattern.compile(EMAIL_PATTERN);

    private final String value;

    @Override
    public String get() {
        return value;
    }

    private Email(String value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Email email = (Email) o;
        return Objects.equals(value, email.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value;
    }

    public static Email of(String value) {
        Objects.requireNonNull(value, "o valor é obrigatório");
        if (!PATTERN.matcher(value).matches()) {
            throw new IllegalArgumentException("Email nao válido");
        }

        return new Email(value);
    }
}

```

Agora iremos utilizar esses tipos na classe `Player`. Ela ficará com o seguinte formato:

```java
import javax.money.MonetaryAmount;
import java.time.Year;
import java.util.Objects;
import java.util.Optional;

public class Player {

    private String id;

    private String name;

    private Year start;

    private Year end;

    private Email email;

    private Position position;

    private MonetaryAmount salary;

//...

}

```

### Builder Pattern 

O segundo e último passo será utilizar o padrão Builder para fazer com que as regras de validação estejam dentro dessa classe. Ao adotar esse padrão, garantiremos que o objeto só será instanciado quando os dados forem realmente válidos, além de colocar a responsabilidade dessa criação em uma classe. Esse padrão é muito interessante porque, além de garantir a responsabilidade única, diminui a chance de um dos parâmetros ser trocado acidentalmente.	 	 

Um ponto importante é que muitos dos frameworks de mapeamento, como o Hibernate, OpenJPA etc., requerem getters e setters, além do construtor padrão. Uma solução para isso seria criar um construtor com todos os parâmetros necessários em privado e um outro como default e com a anotação Deprecated, deixando claro para a pessoa desenvolvedora que aquele construtor não é bem-visto para uso. Veja no exemplo abaixo que o Builder fica como inner class da classe `Player`:

```java
import javax.money.MonetaryAmount;
import java.time.Year;
import java.util.Objects;
import java.util.Optional;

public class Player {

    static final Year SOCCER_BORN = Year.of(1863);

    //hide

    private Player(String name, Year start, Year end, Email email, Position position, MonetaryAmount salary) {
        this.name = name;
        this.start = start;
        this.end = end;
        this.email = email;
        this.position = position;
        this.salary = salary;
    }

    @Deprecated
    Player() {
    }

    public static PlayerBuilder builder() {
        return new PlayerBuilder();
    }

    public static class PlayerBuilder {

        private String name;

        private Year start;

        private Year end;

        private Email email;

        private Position position;

        private MonetaryAmount salary;

        private PlayerBuilder() {
        }

        public PlayerBuilder withName(String name) {
            this.name = Objects.requireNonNull(name, "name is required");
            return this;
        }

        public PlayerBuilder withStart(Year start) {
            Objects.requireNonNull(start, "start is required");
            if (Year.now().isBefore(start)) {
                throw new IllegalArgumentException("you cannot start in the future");
            }
            if (SOCCER_BORN.isAfter(start)) {
                throw new IllegalArgumentException("Soccer was not born on this time");
            }
            this.start = start;
            return this;
        }

        public PlayerBuilder withEnd(Year end) {
            Objects.requireNonNull(end, "end is required");

            if (start != null && start.isAfter(end)) {
                throw new IllegalArgumentException("the last year of a player must be equal or higher than the start.");
            }

            if (SOCCER_BORN.isAfter(end)) {
                throw new IllegalArgumentException("Soccer was not born on this time");
            }
            this.end = end;
            return this;
        }

        public PlayerBuilder withEmail(Email email) {
            this.email = Objects.requireNonNull(email, "email is required");
            return this;
        }

        public PlayerBuilder withPosition(Position position) {
            this.position = Objects.requireNonNull(position, "position is required");
            return this;
        }

        public PlayerBuilder withSalary(MonetaryAmount salary) {
            Objects.requireNonNull(salary, "salary is required");
            if (salary.isNegativeOrZero()) {
                throw new IllegalArgumentException("A player needs to earn money to play; otherwise, it is illegal.");
            }
            this.salary = salary;
            return this;
        }

        public Player build() {
            Objects.requireNonNull(name, "name is required");
            Objects.requireNonNull(start, "start is required");
            Objects.requireNonNull(email, "email is required");
            Objects.requireNonNull(position, "position is required");
            Objects.requireNonNull(salary, "salary is required");

            return new Player(name, start, end, email, position, salary);
        }

    }
}

```

{pagebreak}

Dessa forma, teremos a certeza de que, quando a instância de um jogador (`Player`) for criada, ela será consistente e à prova de falhas.

```java
     CurrencyUnit usd = Monetary.getCurrency(Locale.US);
     MonetaryAmount salary = Money.of(1 _000_000, usd);
     Email email = Email.of("marta@marta.com");
     Year start = Year.now();
     Year end = start.plus(-1, ChronoUnit.YEARS);

     Player marta = Player.builder().withName("Marta")
         .withEmail(email)
         .withSalary(salary)
         .withStart(start)
         .withPosition(Position.FORWARD)
         .build();

```

Podemos aplicar esse mesmo princípio na criação de um time (`Team`):

```java
  Team bahia = Team.of("Bahia");
  Player marta = Player.builder().withName("Marta")
      .withEmail(email)
      .withSalary(salary)
      .withStart(start)
      .withPosition(Position.FORWARD)
      .build();

  bahia.add(marta);

```



### Utilizando Bean Validation

Uma outra maneira de garantir a validação dos dados é utilizando recursos do [Bean Validation](https://beanvalidation.org/). O Bean Validation é uma especificação Java cujo objetivo é garantir que os atributos dentro da classe sejam válidos através da utilização exclusiva de anotações, ou seja, de uma maneira simples e reaproveitável. 

É importante salientar que todas as regras citadas anteriormente continuam válidas com o uso dessa API e que é importante que sejam mantidas todas as boas práticas de orientação a objetos, como encapsulamento. Assim, o uso do Bean Validation pode ser visto como uma dupla verificação ou para que o Builder execute as validações oriundas do framework, de modo que ele só retorne a instância caso todas as validações sejam bem-sucedidas.

```java
import javax.money.MonetaryAmount;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PastOrPresent;
import javax.validation.constraints.PositiveOrZero;
import java.time.Year;
import java.util.Objects;
import java.util.Optional;

public class Player {

    static final Year SOCCER_BORN = Year.of(1863);

    @NotBlank
    private String name;

    @NotNull
    @PastOrPresent
    private Year start;

    @PastOrPresent
    private Year end;

    @NotNull
    private Email email;

    @NotNull
    private Position position;

    @NotNull
    private MonetaryAmount salary;

    @PositiveOrZero
    private int goal = 0;

    //continue

}

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class Team {

    static final int SIZE = 20;

    @NotBlank
    private String name;

    @NotNull
    @Size(max = SIZE)
    private List<Player> players = new ArrayList<>();

    //continue

}

```

O código fonte desse exemplo está disponível no repositório: [https://github.com/soujava/bulletproof](https://github.com/soujava/bulletproof).

> **TIP**: Lembre-se da importância dos testes de unidade em todo o processo de desenvolvimento!

Com a criação desse exemplo, demonstramos que apenas ao utilizar conceitos de orientação a objetos você estará criando um código à prova de falhas. Até esse momento, todas as práticas funcionam de maneira agnóstica ao banco de dados, ou seja, podemos utilizar essas boas práticas independentemente da tecnologia de persistência que será adotada.  

## Lombok: problema ou solução?

De uma maneira geral, o projeto Lombok é uma biblioteca famosa e conhecida por reduzir a quantidade de linhas de código através da utilização de anotações. Essa ferramenta tem seus benefícios, uma vez que a redução de código pode facilitar a leitura. Um exemplo é a anotação `Builder`, que permite a criação de classes mais intuitivas e no padrão Builder. Por outro lado, também vemos alguns problemas no uso desse projeto, itens que estão listados a seguir:

* O código é gerado pelo projeto Lombok e não é visualizado pela IDE. Qualquer exceção que envolva essas classes tenderá a ser difícil de ser seguida pela pilha de exceção;

* Existem anotações como `@Data` que permitem a quebra do encapsulamento das classes. Com isso, você deixará de programar utilizando o paradigma de programação orientada a objetos. É importante salientar as informações trazidas no capítulo 7 do livro Clean Code: a maior diferença entre OOP e programação estruturada é que, na primeira opção, nós escondemos os dados para expor o comportamento;

* Boa parte dos códigos como getters e setters podem ser gerados pela IDE;

* Os poderes das anotações são muito tentadores, porém encapsulamento não é sobre ter o atributo privado e com getter e setter públicos, mas sim sobre garantir que atributos sejam acessados com a menor visibilidade possível.

Queremos deixar claro que o objetivo deste tópico não é classificar o Lombok e seu relacionamento ou não com as boas práticas de programação. A intenção é mostrar que, apesar de possuir suas vantagens - expostas em diversos sites -, é importante ter em mente os problemas acarretados com a adoção dessa tecnologia.



================================================
FILE: manuscript/chapter_04.md
================================================
# Clean Architecture {#chapter_04}

É interessante como os conceitos de Clean Architecture podem ser relacionados em diversos aspectos com o livro de Domain Driven Design, de Eric Evans. Podemos exemplificar esta relação quando, no livro de DDD, é citada diversas vezes a proposta de criação de uma linguagem próxima do negócio: a linguagem ubíqua. Já no livro Clean Architecture, de Robert C. Martin Series, se fala sobre separar o código de negócio do que importa, ou seja, não amarrar regra de negócio com a tecnologia escolhida.

> **INFO**: Clean Architecture é um livro muito bom e faz parte de uma "trilogia" cuja leitura é recomendada: Clean Code: a Handbook of Agile Software Craftsmanship (Robert C. Martin Series), The Clean Coder: a Code of Conduct for Professional Programmers (Robert C. Martin Series) e, finalmente, Clean Architecture: a Craftsman's Guide to Software Structure and Design (Robert C. Martin Series). 
>
> Assumimos que o leitor tenha realizado leitura prévia do livro Clean Architecture. Este capítulo não tem o objetivo de falar sobre o livro, mas expor como utilizamos e aplicamos seus conceitos com uma visão prática. 

Iniciaremos falando sobre a estratégia de dividir e conquistar. Essa estratégia possui muitas vantagens, e vamos citar duas dentre elas:

* **Testes**: Uma das grandes vantagens nessa separação, certamente, se encontra na facilidade de testes, principalmente os testes de unidade. Uma maior separação das camadas facilita, por exemplo, mockar as camadas de infraestrutura e fazer com que o teste seja barato - em outras palavras, fácil de criar e rápido de executar. Por consequência, esses testes tendem a ser executados constantemente, com a facilidade e maior cobertura. 
  É comum vivenciar projetos em que testes levam horas para serem executados, trazendo consequências negativas. Testes que falham constantemente tendem a ser ignorados, e testes não confiáveis farão com que o time nunca saiba se houve um problema de regressão ou se simplesmente é aquele “erro amigo”. 

> **TIP:** Vale salientar que não estamos criticando outros tipos de testes; porém, testes unitários têm vantagens. Ao considerarmos que tecnologias como Hibernate, Java e JPA possuem seus próprios testes, lembre-se de que o que você de fato testar são as regras do seu negócio.

* **Baixo impacto em mudanças**: Uma separação maior do negócio da tecnologia acarreta menores impactos em casos de mudança de tecnologia, como, por exemplo, uma troca entre vendors de banco de dados. 

> **TIP:** Na área de arquitetura, o pragmatismo é uma característica crucial. O maior foco de um arquiteto é resolver um problema usando a tecnologia como meio, não como fim. Deste modo, usuários(as) finais não precisam, e muitas vezes, não querem saber qual o banco ou linguagem estão sendo utilizados.

Observe a imagem a seguir:

![Imagem 04_01: As diferentes camadas da aplicação e como as práticas do Clean Architecture pregam que devem ocorrer as interações entre estas camadas. ](images/chapter_04_01.png)

*Fonte: <https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html>*

Usaremos a imagem acima para discorrer sobre pontos de atenção que vale a pena aplicar em sua arquitetura:


* Tenha uma estratégia de teste eficaz, que siga a pirâmide de testes;
* As estruturas devem ser isoladas em módulos individuais. Quando (não se) mudarmos de ideia, precisaremos fazer mudança em apenas um lugar;
* "Arquitetura Gritante" também conhecida como uso pretendido. Ao olhar para a estrutura do pacote, percebe-se imediatamente o que o aplicativo faz, e não seus detalhes técnicos. É semelhante a quando se utiliza package by layer ao invés de package by feature. 

* Toda a lógica de negócios deve estar em um caso de uso, portanto, será fácil encontrar e não duplicar em nenhum outro lugar;

* Será um bom monolito com casos de uso claros que você poderá dividir em microsserviços mais tarde, depois de aprender mais sobre eles.

## Granularidade de camadas

Algo bastante discutido em livros de arquitetura em geral é a granularidade de camadas. Com o tempo se percebe que as camadas podem ajudar tanto em abstração e separação de responsabilidade de negócio quanto no aumento da complexidade do código. 

> **TIP:** Avalie sempre ao colocar mais camadas em uma aplicação e tenha atenção para que elas não se tornem uma arma de destruição ao invés de um item de ajuda.

A estratégia descrita pelo livro Clean Architecture é uma visão de “fora para dentro”, ou seja, a camada framework acessa a camada de adaptação seguindo a linha do princípio da dependência. Descreveremos a seguir, de uma maneira geral, as camadas.

### Entidades

Caso você venha do DDD, não verá muita novidade nessa camada e nos conceitos. Ela é responsável por encapsular o domínio do negócio. O ponto principal é que essa camada é o core, ou seja, é a razão de fazer a aplicação em si. Ou seja, é nela que se concentram as regras de negócio e não deve mudar de acordo com itens externos, como, por exemplo, mudança de banco de dados.

>  **TIP:** Particularmente, não achamos "crime federal" caso existam tecnologias que tendem a não mudar, por exemplo, bibliotecas utilitárias usadas por toda a empresa. Em uma aplicação menor, podem ser apenas interfaces e classes que tenham que ser utilizadas em todas as camadas, como a interface de um repositório.

### Casos de uso

Nesta camada se concentram as ações da regra de negócios. Uma maneira de pensar é que esta seja uma continuação da camada de entidade. Muitas regras que envolvem as entidades ficam muito grandes para caberem apenas na entidade, isso sem mencionar o clássico problema de responsabilidade única que tanto falamos no [SOLID](https://en.wikipedia.org/wiki/SOLID).

### Interface de adaptação

Um ponto de vista interessante é que essa camada é uma grande implementação do padrão de projeto [Adapter](https://refactoring.guru/design-patterns/adapter). O seu maior objetivo é deixar as camadas de entidades e Casos de Uso mais transparentes. Por exemplo, uma interface repositório pode ter diversas implementações, seja uma base de dados relacional, seja não relacional. 

O maior objetivo dessa camada é garantir que as mudanças de tecnologia não impactem as outras camadas. Afinal, para o(a) usuário(a) não importa se o banco de dados é um Cassandra ou um PostgreSQL, mas, num nível técnico, é importante pensar nas diferentes estratégias de modelagem.

### Frameworks


Esta é a camada que “não importa” para o negócio. Em outras palavras, é a camada para o "meio" e não para o "fim". Ela é composta por ferramentas e tecnologias como banco de dados. O maior ponto para a estratégia dessa camada é evitar que ela passe para o menor número possível de camadas. É importante salientar que quanto menos código nessa camada melhor, ou seja, ele terá o necessário para interligar as tecnologias.



## Conclusão

O livro Clean Architecture traz uma boa referência para se aplicar em arquiteturas maduras e uma melhor estratégia de como utilizar e comunicar entre as camadas. Um ponto importante: não existe bala de prata, e o livro em questão também não é um. O material é bastante rico e cheio de detalhes, porém, lembre-se de que camadas tendem a aumentar a complexidade do seu código, pois quanto mais camadas são criadas, mais camadas são mantidas. Como diria o livro [fundamentos de arquitetura de software](https://www.amazon.com/Fundamentals-Software-Architecture-Comprehensive-Characteristics/dp/1492043451), tudo é um trade-off, e o bom senso ainda é a melhor ferramenta para que o(a) arquiteto(a) saiba *quando* e *como* aplicar conceitos e práticas.

================================================
FILE: manuscript/chapter_05.md
================================================
# Refatoração {#chapter_05}

Após alguns anos de estrada, quem desenvolve percebe que passa grande parte do seu tempo lendo código e, na maioria das vezes, código escrito por outras pessoas. Sem muito esforço, percebe-se que esse tempo é superior ao tempo gasto escrevendo novas linhas de código, afinal de contas, é preciso entender o funcionamento do código atual, onde deverá fazer suas mudanças, e, principalmente, quais classes ou arquivos serão impactados por elas.

Não à toa, uma simples alteração no código, como adicionar um `if`, pode levar horas ou mesmo dias. Geralmente, esse alto custo para manter o software é consequência de uma péssima qualidade do código que foi escrito, afinal, quanto maior a dificuldade em ler e entender um trecho de código, maior será o tempo gasto para alterá-lo. E não se engane, mesmo um(a) dev que implementou a funcionalidade no sistema pode ter dificuldades para ler o código após alguns meses.

Para diminuir o custo nas alterações do sistema, é necessário **investir na qualidade e na clareza do código produzido**, seja no código atual, seja na introdução de novas linhas de código. Em outros termos, deve-se melhorar o código já pronto e que funciona em produção sem mudar o seu comportamento. Essa técnica, conhecida por grande número de profissionais, é chamada de **Refatoração** (ou, em inglês, Refactoring).

Uma das definições mais aceitas na indústria para "Refatoração" é a de **Martin Fowler**, em seu livro **Refactoring: Improving the Design of Existing Code**, segundo a qual:

> "Refatoração é uma técnica controlada para reestruturar um trecho de código existente, **alterando sua estrutura interna sem modificar seu comportamento externo**. Consiste em uma série de pequenas transformações que preservam o comportamento inicial. Cada transformação (chamada de refatoração) reflete em uma pequena mudança, mas uma sequência de transformações pode produzir uma significante reestruturação. Como cada refatoração é pequena, é menos provável que se introduza um erro. Além disso, o sistema continua em pleno funcionamento depois de cada pequena refatoração, reduzindo as chances de o sistema ser seriamente danificado durante a reestruturação." -- *Martin Fowler*

Com base nas palavras de Fowler, podemos entender que refatoração é o processo de modificar um trecho de código já escrito, executando pequenos passos (**baby steps**) sem modificar o comportamento atual do sistema. É uma técnica utilizada para melhorar algum aspecto do código, entre os quais podemos citar melhorias na clareza do código para facilitar a leitura, ou também ajustes no design das classes a fim de trazer maior flexibilidade ao sistema.

## Medo de alterar o código que não é seu

Se você trabalha com desenvolvimento, em algum momento da sua carreira, entrou ou entrará no meio de um projeto de software. Isso quer dizer que você chegou (ou chegará) de paraquedas para desenvolver, corrigir e manter funcionalidades em cima de uma base de código existente, que pode ter desde 6 meses até 20 anos de vida. 

Essa base de código provavelmente já passou pela mão de diversas outras pessoas, desde quem tem mais experiência até quem tem nível mediano e quem faz estágio, geralmente sob pressão e prazos surreais e apertados. Para piorar, dificilmente a equipe que iniciou o projeto e tomou decisões importantes de arquitetura e de design estará ainda na empresa. Ou seja, um sistema com 5 anos de idade provavelmente sofreu trocas no time completo 2 ou 3 vezes - no mínimo.

Essa alta rotatividade de profissionais traz inúmeros prejuízos à empresa, em especial ao código do sistema. Isso porque certos trechos de código críticos foram (e são) alterados, mantidos e evoluídos por diversos profissionais, desde quem domina o negócio de ponta a ponta a quem nem tanto; desde quem tem maestria nas tecnologias e frameworks utilizados a quem não tem a mínima ideia de como eles funcionam; e há também quem tenha utilizado o projeto como laboratório no curto tempo que permaneceu na empresa. Não é de se espantar que boa parte desse código seja repleto de gambiarras e duplicação de rotinas, abarrotada de comentários desatualizados, pouca clareza nas lógicas de negócio, alto acoplamento e baixa coesão. Não se assuste ao ter que manter uma classe ou um método com mais de 5 mil linhas de código macarrônico e totalmente ilegível.

Situações como essa são mais comuns do que se pode imaginar. Lidar com sistemas problemáticos dessa magnitude traz frustração e, principalmente, **medo** para um(a) dev que acaba de entrar na empresa, que terá que trabalhar no código "dos outros", programando no escuro, sem ter a mínima noção se sua última alteração impacta noutras partes do sistema ou, pior ainda, qual o tamanho desse impacto.

## A importância dos testes automatizados na hora refatorar

Quanto menos conhecimento sobre sistema o(a) desenvolvedor(a) tem, maior é sua insegurança na hora de alterar código e menores são as garantias de que suas alterações não causarão mais estragos ao sistema, que já se encontra frágil pelo tempo. Mesmo pequenas refatorações com o intuito de melhorar a clareza ou a simplicidade do código podem gerar novos bugs, reintroduzir bugs antigos no sistema ou impactar o sistema de forma negativa.

Se refatorar código traz riscos, como se pode garantir que erros não serão introduzidos nas suas refatorações?

A verdade é que refatorar código sem uma garantia concreta de que o comportamento atual não vá mudar é um ato imprudente, pois não adianta nada melhorar o código se algo que já existe é quebrado. Essa garantia pode ser obtida de diversas maneiras, como uma equipe de QA (testers) ou um ambiente de homologação para que clientes possam validar a mudança, mas sem dúvida uma das melhores alternativas a um preço justo é através do uso de **Testes Automatizados**. Uma bateria de testes de regressão mostraria rapidamente se uma determinada mudança (ou refatoração) no código mudou o comportamento da funcionalidade ou mesmo impactou outras partes do sistema. Com isso, qualquer erro introduzido seria imediatamente apontado, facilitando a correção a cada passo da refatoração de maneira imediata.

Como dito por Fowler, refatorar é o ato de aplicar pequenas transformações no código existente sem mudar seu comportamento externo. Assim como muitas técnicas de engenharia de software, a teoria é mais fácil do que a prática. Em um processo complexo, como desenvolvimento de software, temos que verificar frequentemente onde estamos e, se necessário, fazer correções no percurso. No caso da refatoração, a cada transformação precisamos verificar que tudo continua funcionando como esperado para só então partirmos para o passo seguinte. O contrário também é verdade: precisamos identificar se nosso último passo quebrou algo no sistema para então desfazê-lo e adotar uma estratégia diferente. Esse ciclo frequente de verificação em que aprendemos a cada passo recebe o nome de **Feedback Loop**. No mundo de metodologias ágeis, o *Feedback* funciona como a engrenagem motora para todos os quatro valores do Manifesto Ágil e suas derivações encontradas no mercado.

Quanto mais curto e frequente o feedback loop, mais natural o processo de refatoração se torna. Mas como encurtá-lo? Bem, existem diversas práticas e ferramentas que podem ajudar, como pair programming, servidor de integração contínua (CI) ou code review, mas entre elas eu quero destacar a cobertura de testes. Quando temos código coberto por testes, esse ciclo é encurtado de tal forma que fica natural para quem desenvolve repeti-lo a todo momento. Basta um teste de unidade quebrar que sabemos de imediato o que foi feito de errado. Perceba que os testes funcionam como uma rede de segurança para encorajar quem desenvolve a refatorar código e, principalmente, a manter a motivação para tornar essa prática uma constante no seu dia a dia.

Não vou mentir, é possível fazer refatorações sem uma linha de teste automatizado, muitas empresas e equipes fazem isso; contudo, não se pode ignorar que há grandes riscos envolvidos nessa prática que podem introduzir bugs ou gerar prejuízos para a empresa ou cliente final. Refatorar código sem testes torna tudo mais difícil e arriscado, consome mais tempo do que o necessário, desencoraja melhorias no código e ainda exige muito mais da pessoa desenvolvedora e da equipe. Para refatorar sem testes, a experiência de quem desenvolve conta bastante, seja para fazer mudanças com passos pequenos e seguros, analisar e mensurar o impacto de suas (possíveis) mudanças, seja para simplesmente decidir que não vale a pena refatorar determinado trecho de código.

Apesar de a experiência de quem desenvolve ser um fator importante na hora de refatorar código não coberto por testes, dependendo da empresa e dos processos adotados por ela, as chances são de que gestores não permitam modificar um código que funciona e já se encontra em produção.

O tema testes automatizados é muito amplo e merece um capítulo completo ou mesmo um livro - e por sinal existem alguns muito bons. Mas para não alongar neste tópico, deixo o seguinte questionamento: **na hora de refatorar, quão longe podemos ir, ou quão ousados podemos ser sem uma boa cobertura de testes?**

## Refatoração contínua do código

Um ponto importante é que dificilmente a empresa ou stakeholder permitirá que se crie uma tarefa inteira para realizar a refatoração de um projeto. Afinal, para o(a) usuário(a) final, pouco impacta a tecnologia ou a qualidade de código que o projeto está usando. Uma boa estratégia é a famosa técnica de escoteiro. Essa técnica se baseia em refatorar os códigos relacionados que precisam de melhoria e clareza, ao passar por uma história que agrega valor para o produto. Sempre salientando que, por segurança, é importante adicionar testes antes de fazer qualquer refatoração.

## Qual sua motivação para refatorar o código?

Existem diversas motivações para realizar uma refatoração do código, e listamos aqui alguns dos principais:

* **Refatorando para legibilidade e flexibilidade**: Legibilidade traz diversos benefícios para o ciclo de vida de uma aplicação, seja como agilidade na manutenabilidade, seja como facilidade de depurar, além do fator de bem-estar dentro do time. Vale ressaltar que essa manutenção dificilmente entrará como uma história ou atividade, porém, se abster totalmente nessa atividade acarreta o fator "Janelas partidas", ou seja, à medida que o código for piorando a legibilidade, maior é a tendência de que a próxima pessoa que realizar manutenção abrirá mão da qualidade para se livrar dessa atividade o mais rápido possível até que o código seja um risco pelo fator da falta de legibilidade.
* **Refatorando para performance**: Performance é sempre importante, porém, deve-se considerar se essa melhoria resultará em maior complexidade ou algum outro eventual problema de consistência de dados. Como diria Donald Knuth, "a otimização prematura é a raiz de todo o mal". Assim, toda melhoria de performance é importante, porém leve em consideração os seus impactos e se isso faz sentido nesse momento.
* **Refatorando para remover duplicidade**: Reduzir código dentro do projeto é uma boa motivação. Quando somos juniores tendemos a ficar felizes quando adicionamos código, e quando ficamos mais experientes ficamos felizes com a redução de código de que precisamos. Uma das estratégias de reduzir o código é, justamente, evitar a duplicação de código. Isso te garantirá um único ponto para refatoração: facilidade de teste, além de performance, principalmente se você trabalha dentro da JVM, uma vez que existe o fator JIT.
* **Refatorando para usar uma biblioteca**: O melhor código, certamente, é aquele que não escrevemos. Reduzir a quantidade de código siginifica reduzir a complexidade do seu lado, além de diminuir os pontos que darão erro. Isso é excelente, principalmente quando essa biblioteca já esteja disponível dentro do seu projeto. Vale salientar que excesso de dependência também pode ser perigoso. Lembre-se de que, quanto maiores as suas dependências, maior a estratégia para atualizar as bibliotecas (além do conhecido problema de "Jar Hell").

## Conclusão    

Com isso concluímos o tópico sobre refatoração do código. Abordamos pontos importantes, como a motivação de se refatorar o código além dos seus riscos. Como bons profissionais de desenvolvimento, é sempre importante colocar na balança as motivações e evitar problemas futuros de manutenabilidade e legibilidade do código. Como sempre, o bom senso será sua melhor ferramenta.

================================================
FILE: manuscript/chapter_06.md
================================================
# NoSQL vs. SQL {#chapter_06}

Muitas das vezes, quando iniciamos um debate sobre SQL (Structured Query Language) e NoSQL no meio técnico, já podemos esperar o surgimento de discussões acaloradas. Isso se deve ao fato, principalmente, de que existe uma ideia de que há uma guerra, uma rixa etc. que precisa ser vencida entre quem faz uso dessas tecnologias. Já queremos deixar claro, não existe guerra alguma! Vamos desfazer esse mito?

Para começar, precisamos falar um pouquinho de história. Com isso, entenderemos os detalhes sobre o NoSQL e suas diferenças para o SQL.

Assim como sempre ocorre no universo da tecnologia, nós criamos uma solução para resolver um problema específico. Vamos voltar mais de quarenta anos no tempo para entender o problema existente no contexto dos bancos de dados:

- Para que pudessem ser utilizados posteriormente, os dados produzidos pelos computadores deveriam ficar armazenados e não poderiam ser perdidos quando as máquinas fossem desligadas.

Porém, naquela época, a tecnologia de armazenamento estava engatinhando, e os discos rígidos eram muito caros. Foi necessário então estruturar e normalizar os dados que seriam gravados nesses discos. Com a adoção dessa prática, os dados armazenados utilizariam menos espaço e, consequentemente, trariam um aproveitamento melhor de recursos, gerando economia ou a possibilidade de se armazenar maior quantidade de dados em um mesmo espaço. 

Esse foi o contexto em que os bancos de dados relacionais foram criados, inclusive implementando todos os conceitos detalhados no capítulo anterior, como formas normais e estrutura de dados bem definidas.

O tempo passou, nossa tecnologia evoluiu e, com essa evolução, mais um problema surgiu: produzimos dados em uma quantidade muito grande, muitas vezes de forma desestruturada e também descentralizada, com sistemas cada vez mais distribuídos. 

> **TIP:** Esses dados são chamados de *desestruturados* por terem origem em diversas fontes, como sensores IOT (Internet of Things, i.e. geladeiras conectadas à internet, relógios inteligentes e carros autônomos), imagens e documentos não catalogados, dentre outros exemplos.

Estruturar, isto é, organizar os dados provenientes de fontes como estas era (e é) possível, porém iria requerer muito tempo. Esse tempo extra impactaria o processo de desenvolvimento e entrega de software e, consequentemente, levaria as empresas a perderem o time to market da solução sendo criada. Esse problema precisava ser resolvido e, assim, nasceram os bancos de dados NoSQL!

O termo **NoSQL** foi originalmente criado em 1998 por Carlo Strozzi e, posteriormente, reintroduzido por Eric Evans em 2009, quando este participou da organização de um evento para discutir bancos de dados **open source** e **distribuídos**. E por falar em bancos distribuídos, esse é um conceito amplamente utilizado pelos bancos NoSQL: basicamente, os bancos NoSQL são bancos de dados que operam em computação distribuída, o que impulsiona um significativo grau de escalabilidade e performance. 

> **TIP:** Para entender um pouco mais sobre computação distribuída, recomendamos a leitura do seguinte artigo: [Paradigma da computação distribuída](https://imasters.com.br/arquitetura-da-informacao/paradigma-da-computacao-distribuida).

## NoSQL

### O que significa NoSQL?

Não existe uma definição, digamos, "oficial" para o que realmente esse termo significa, mas particularmente gosto da seguinte: **N**ot **O**nly **SQL**, ou seja, "Não Somente SQL". Essa definição enfatiza que esses bancos podem utilizar linguagens semelhantes ao [SQL ANSI](https://pt.wikipedia.org/wiki/SQL) para realizar consultas e demais operações e não somente o SQL em si.

### Particularidades do NoSQL

Quando você estudou sobre bancos de dados relacionais, viu que esses bancos são baseados no conceito **ACID** (**A**tomicity, **C**onsistency, **I**solation, **D**urability). Os bancos de dados NoSQL, em sua grande maioria, baseiam-se em um outro conceito: o **BASE** (**Ba**se Availability, **S**oft State and **E**ventually Consistent).

Antes de detalhar cada ponto do conceito **BASE**, você precisa entender a representação de um termo no contexto de banco de dados que verá ao longo deste capítulo: **cluster**. Um cluster, nesse contexto, se refere à capacidade de um conjunto de **servidores** (de banco) ou **instâncias** (de banco) se conectarem a um banco de dados. Uma **instância** é uma coleção de memória e processos que interagem com o banco de dados, que é o conjunto de arquivos físicos que efetivamente armazenam os dados.

Devemos destacar duas principais vantagens de um cluster, especialmente em ambientes de banco de dados de alto volume:

* **Tolerância a falhas** (_Fault Tolerance_): como há mais de um servidor ou instância para os usuários se conectarem, o cluster de bancos de dados oferece uma alternativa no caso de falha em um servidor. Quando se lida com dezenas de milhares de máquinas em um único [_data center_](https://pt.wikipedia.org/wiki/Centro_de_processamento_de_dados), tais falhas são um problema presente;

* **Balanceamento de carga** (_Load Balancing_): o cluster geralmente é configurado para permitir que a aplicação cliente seja automaticamente alocada ao servidor com o mínimo de uso para que, assim, se otimize o uso da estrutura disponível para o banco.

#### Detalhando o conceito BASE

- Base Availability - **BA**
    - O banco de dados aparenta funcionar o tempo todo. Como existe a implementação do conceito de cluster, se um servidor falhar, o banco continuará funcionando por conta de outro servidor que suprirá essa falha;

- Soft State - **S**
    - Não é necessário estar consistente o tempo todo. Ou seja: com um banco distribuído em várias máquinas e todas sendo usadas com igual frequência para escrita e consulta, é possível que, em dado momento, uma máquina receba uma escrita e não tenha tido tempo de "repassar" essa escrita para as demais máquinas do banco. Assim, se uma aplicação consultar a máquina que já foi atualizada e outra o fizer numa máquina menos atualizada, os resultados, que deveriam ser iguais, serão diferentes. Imagine a sua _timeline_ do **Facebook**: nela são exibidos os posts, porém nem todos os posts são exibidos exatamente ao mesmo tempo. Nesse caso, o que acontece é que a informação foi enviada ao banco de dados, mas nem todos os servidores do cluster têm essa mesma informação ao mesmo tempo. Isso permite que o banco de dados possa gerenciar mais informações de escrita sem ter que se preocupar em replicá-las em uma mesma operação;

      {pagebreak}
    
- Eventually Consistent - **E**
    - O sistema se torna consistente em algum momento. Em outras palavras, ele eventualmente se tornará consistente. Como não temos a informação replicada "instantaneamente", esse conceito se encarrega de deixar o banco consistente "ao seu tempo". Isso porque, dependendo das configurações do cluster, essa replicação pode acontecer mais rapidamente ou não. Mas em algum momento as informações estarão consistentes e presentes em todos os servidores do cluster.

Para finalizar, uma outra particularidade marcante de bancos não estruturados é a ausência da feature de `schema` ou `schema flexível`. Isso quer dizer que não há necessidade de definição prévia do `schema` dos dados. Se, por um lado, isso torna mais dinâmico o processo de inclusão de novos atributos, por outro pode impactar a integridade desses dados. Não se preocupe: a seu tempo, todos esses conceitos ficarão bem mais claros.

---

## NoSQL e suas classes

Os bancos de dados NoSQL podem ser categorizados em quatro tipos (que, no contexto de banco de dados, são chamados de **classes**):

* Chave / Valor - `Key / Value`
* Família de Colunas - `Column Family`
* Documentos - `Document`
* Grafos - `Graph`

Cada classe acima possui uma aplicação diferente, portanto, devemos entender as características de cada classe para tirar o melhor proveito de cada uma e saber quando escolhê-las.

#### Classe `Key / Value`

![Imagem 06_01: Exemplo de estrutura de dados em um banco Key/Value](images/chapter_06_01.png)

Os bancos do tipo chave-valor possuem uma estrutura similar à da classe `java.util.Map` do Java, ou seja, a *informação* (`value`) será recuperada apenas pela *chave* (`key`). Esse tipo de banco de dados pode ser utilizado, por exemplo, para gerenciar sessões de usuários logados. Outro exemplo de utilização é aliado a um DNS, onde a chave é o endereço, por exemplo, `www.google.com`, e o valor é o IP desse servidor.

Atualmente existem diversas implementações de banco de dados do tipo chave-valor, dentre os quais os mais famosos são:

* AmazonDynamo
* AmazonS3
* Redis
* Scalaris
* Voldemort

Comparando o banco de dados relacional com o do tipo chave-valor, podemos elencar alguns pontos de atenção. O primeiro é que a estrutura do chave-valor é bastante simples:

| Estrutura relacional | Estrutura chave-valor |
| -------------------- | --------------------- |
| Table                | Bucket                |
| Row                  | Key/value pair        |
| Column               | ----                  |
| Relationship         | ----                  |

Nessa classe de banco, não é possível realizar operações como `join` entre os `buckets`, e o valor é composto por um grande bloco de informação, ao invés de ser subdivido em colunas, como ocorre na base de dados relacional.

#### Classe `Column Family`

![Imagem 06_02: Exemplo de estrutura de dados armazenadas em um banco Column Family.](images/chapter_06_02.png)

Esse modelo se tornou popular através do paper "[Bigtable: a Distributed Storage System for Structured Data](https://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf)", criado por funcionários do Google, com o objetivo de montar um sistema de armazenamento de dados distribuído, projetado para ter um alto grau de escalabilidade e de volume de dados. Assim como o chave-valor, para realizar uma busca ou recuperar alguma informação dentro do banco de dados, é necessário utilizar o campo que funciona como um identificador único, que seria semelhante à chave na estrutura chave-valor. Porém as semelhanças terminam por aí. 

Em bancos do tipo `Column Family`, as informações são agrupadas em colunas: uma unidade da informação (composta pelo nome) e a informação em si.

Esses tipos de bancos de dados são importantes quando se lida com um alto grau de volume de dados, e quando é necessário distribuir essas informações entre diversos servidores. Mas vale salientar que a sua operação de leitura é bastante limitada, semelhante ao chave-valor, pois a busca da informação é definida a partir de um campo único ou uma chave. Existem diversos bancos de dados que utilizam essas estruturas. Podemos citar:

* Hbase
* Cassandra
* Scylla
* Clouddata
* SimpleDb
* DynamoDB

Dentre os tipos de bancos de dados do tipo família de coluna, o Apache Cassandra é o mais famoso. Caso uma aplicação necessite lidar com um grande volume de dados e com fácil escalabilidade, o Cassandra é certamente uma boa opção.


Ao contrapor o banco do tipo família de coluna com os bancos relacionais, é possível perceber que as operações, em geral, são muito mais rápidas. É mais simples trabalhar com grandes volumes de informações e servidores distribuídos em todo o mundo, porém isso tem um custo: a leitura desse tipo de banco de dados é bem limitada. Por exemplo, não é possível realizar uniões entre família de colunas como no banco relacional. A família de coluna permite que se tenha um número ilimitado de coluna, que por sua vez é composta por nome e a informação, exatamente como mostra a tabela a seguir:

| Estrutura relacional | Estrutura de família de colunas |
| -------------------- | ------------------------------- |
| Table                | Column Family                   |
| Row                  | Column                          |
| Column               | Nome e valor da Column          |
| Relacionamento       | Não tem suporte                 |

#### Classe `Document`

Os bancos de dados orientados a documento têm sua estrutura muito semelhante a um arquivo JSON ou XML. Eles são compostos por um grande número de campos, que são criados em tempo de execução, gerando grande flexibilidade, tanto para a leitura como para escrita da informação. 

{width=60%}

![Imagem 06_03: Estrutura do dado armazenado em um banco orientado a documentos](images/chapter_06_03.png)

Eles permitem que seja realizada a leitura da informação por campos que não sejam a chave. Algumas implementações, por exemplo, têm uma altíssima integração com motores de busca, o que os torna cruciais para a realização de análise de dados ou logs de um sistema. Veja abaixo algumas implementações dos bancos de dados do tipo documento:

* AmazonSimpleDB
* ApacheCouchDB
* MongoDB
* Riak


Destes, o mais popular é o MongoDB. Ao comparar com uma base relacional, apesar de ser possível realizar uma busca por campos que não sejam o identificador único, os bancos do tipo documentos não têm suporte a relacionamento. 

| Estrutura relacional | Estrutura de documentos |
| -------------------- | ----------------------- |
| Table                | Collection              |
| Row                  | Document                |
| Column               | Key/value pair          |
| Relationship         | --                      |

Uma outra característica de bancos do tipo documento é que, no geral, são _schemeless_.

#### Classe `Graph`

![Imagem 06_04: Como os dados são armazenados em um banco de classe Grafo.](images/chapter_06_04.png "Estutura de Grafos")

O banco do tipo grafo é uma estrutura de dados que conecta um conjunto de vértices através de um conjunto de arestas. Os bancos modernos dessa categoria suportam estruturas de grafo multirrelacionais, em que existem diferentes tipos de vértices (representando pessoas, lugares, itens) e diferentes tipos de arestas. Os sistemas de recomendação que acontecem em redes sociais são o maior case para o banco do tipo grafo. Veja abaixo alguns exemplos desse tipo de banco:


* Neo4j
* InfoGrid
* Sones
* HyperGraphDB

Dos tipos de banco de dados mais famosos no mundo NoSQL, o grafo possui uma estrutura distinta com o relacional.

#### Multi-model database

Alguns bancos de dados possuem a comum característica de ter suporte de uma ou mais classes apresentadas. São exemplos:

* OrientDB
* Couchbase

### Teorema do CAP

{width=60%}

![Imagem 06_05: Teorema do CAP.](images/chapter_06_05.png)

Um dos grandes desafios dos bancos de dados NoSQL é que eles lidam com a persistência distribuída, ou seja, as informações ficam localizadas em mais de um servidor. Foram criados diversos estudos para ajudar nesse desafio de persistência distribuída, mas o mais famoso foi uma teoria criada em 1999, o Teorema do CAP. 

Esse teorema afirma que é impossível que o armazenamento de dados distribuído forneça simultaneamente mais de duas das três garantias seguintes:

* *Consistência*: uma garantia de que cada nó em um cluster distribuído retorna a mesma gravação mais recente e bem-sucedida. Consistência refere-se a cada cliente com a mesma visão dos dados.
* *Disponibilidade*: cada pedido recebe uma resposta (sem erro) - sem garantia de que contém a escrita mais recente.
* *Tolerância à partição*: o sistema continua a funcionar e a manter suas garantias de consistência, apesar das partições de rede. Os sistemas distribuídos que garantem a tolerância continuam operando, mesmo que aconteça alguma falha em um dos nós, uma vez que existe, pelo menos, um nó para operar o mesmo trabalho e garantir o perfeito funcionamento do sistema.

De uma maneira geral, esse teorema explica que não existe mundo perfeito. Quando se escolhe uma característica, perde-se em outra, como consequência. Em um mundo ideal, um banco de dados distribuído conseguiria suportar as três características, porém, na realidade, é importante saber o que se perderá quando escolher entre um e outro.

Por exemplo, o Apache Cassandra é AP, ou seja, sua arquitetura focará em tolerância a falha e disponibilidade. Existirão perdas na consistência, assim, em alguns momentos um nó retornará informação desatualizada.

Porém, o Cassandra tem o recurso de nível de consistência, de modo que é possível fazer com que algumas requisições ao banco de dados sejam enviadas a todos os nós ao mesmo tempo, garantindo consistência. Vale ressaltar que, fazendo isso, ele perderá o `A`, de _aviability_ do teorema do CAP, da disponibilidade.



### Escalabilidade vs Complexidade

No mundo NoSQL, cada classe de banco tem o objetivo de resolver problemas particulares. Como o gráfico abaixo mostra, existe um balanço entre o modelo de complexidade: modelos que permitem mais complexidade em modelagem e busca resultam en menos escalabilidade. Como exemplo, temos o banco de classe chave-valor, que é mais escalável, porém permite menos complexidade, uma vez que as queries são baseadas apenas na chave.

![Imagem 06_06: Mapeamento dos tipos de banco de dados e a matriz Escalabilidade vs Complexidade. ](images/chapter_06_06.png)

### Master/Slave vs Masterless

Em linha geral, a persistência no mundo NoSQL possui duas maneiras de comunicação entre os servidores:



![Imagem 06_07: Estrutura de banco Master/Slave e Masterless.](images/chapter_06_07.png "Master/Slave vs Masterless")

* *O Master/Slave*: é o modelo de comunicação que se caracteriza por um controle unidirecional de um ou mais dispositivos. Em linhas gerais, o nó master é utilizado para a escrita e para a replicação das informações para todos os nós escravos, que, por sua vez, são responsáveis por realizar a leitura das informações. Dessa maneira, é possível garantir maior consistência de dados. Como há um único ponto para a escrita, é possível ter suporte a comportamentos como, por exemplo, transação. Porém existe um ponto de falha: o master. Caso o servidor fique fora do ar, teremos problemas com a escrita. Em cenários como este, bancos de dados modernos conseguem realizar a eleição de um novo nó master de maneira automática.
* *Masterless*: é o modelo de comunicação que se caracteriza por um controle multidirecional por um ou mais dispositivos. Ou seja, não existe um único nó responsável por leitura ou escrita. Cada nó pode ser responsável pelas duas operações. Assim, não existe nenhum ponto de falha, a elasticidade acontece de maneira natural, porém a consistência da informação se torna mais difícil, uma vez que é necessário um certo tempo para que os nós tenham a informação mais atualizada.

## Conclusão

Este capítulo teve como objetivo dar o pontapé inicial para os bancos de dados não relacionais. Foram discutidos os principais conceitos acerca de bancos não estruturados, as classes de bancos e suas estruturas. Com esse novo paradigma de persistência não estruturada, as portas se abrem para novas possibilidades e novos desafios na implementação de aplicações. 

Os bancos de dados NoSQL vieram para enfrentar e sustentar a nova era das aplicações, na qual velocidade e menor tempo de resposta são um grande diferencial e fator decisivo na hora da escolha. Com este capítulo introdutório, você está apto (a) para seguir desbravando os bancos não relacionais, como o Cassandra, MongoDB, Neo4J e vários outros.

================================================
FILE: manuscript/chapter_07.md
================================================
# Arquitetura de microsserviços {#chapter_07}

Com a popularização da utilização de ambientes de cloud para entrega de software, a [arquitetura orientada a microsserviços](https://www.martinfowler.com/articles/microservices.html) passou a ser cada vez mais adotada. O quão confortável a comunidade de TI pode se sentir acerca da adoção dessa arquitetura? 

Com base na análise de [tendências de arquitetura e design de software realizada em Abril de 2020](https://www.infoq.com/articles/architecture-trends-2020/), podemos assumir que o conhecimento acerca dos benefícios e desafios da adoção desse modelo estão bem estabelecidos, uma vez que sua adoção já chegou à categoria de usuários(as) classificados(as) como "Late Majority". Existem várias histórias de sucesso e de desastre de organizações que optaram pelo uso desse modelo, portanto, nosso ecossistema se encontra repleto de conhecimento como "por onde começar" e "lições aprendidas". 

Antes de entrar em mais detalhes sobre essa arquitetura, vamos brevemente recapitular os desafios da arquitetura monolítica que antecederam a criação desse novo modelo arquitetural.  

## Arquitetura monolítica

Em uma arquitetura monolítica encontramos uma aplicação cujos front-end e back-end são parte de um artefato único. Nesse artefato estão contidos todos componentes funcionais, que são compilados e disponibilizados em conjunto. A escalabilidade é impactada, uma vez que, sempre que é necessário escalar essa aplicação, é necessário também prover recursos para a execução de todos os seus componentes - mesmo aqueles que não precisavam ser escalados. No cenário de persistência, é muito comum se encontrar a relação de um banco para um monolito, porém existem monolitos que trabalham acessando múltiplos bancos de dados (o que aumenta ainda mais o nível de complexidade de manutenção).

![](images/chapter_07_01.png)

Vamos falar dos benefícios dessa arquitetura:

- Facilidade de manutenção, afinal, quanto menos camadas físicas, menos pontos para se verificar, especialmente quando ainda está pequeno;
- Facilidade de sincronizar os dados entre a pessoa usuária e o sistema - a melhor e mais rápida maneira de trocar informações é pela memória. Pensando num banco de dados relacional, por exemplo, a sincronização de dados acontece de maneira mais simples com os clássicos JOINs, ao invés de realizar todo o processo de sincronização ou orquestração entre serviços.
- Consistência de dados tende a ser facilitada em comparação a um sistema distribuído. Salientamos que sempre teremos o problema do CAP quando falamos de arquitetura distribuída. Para aplicações que precisam, por exemplo, transação, no que no monolito seria um simples rollback, num sistema de microservices seria o caro e complexo padrão SAGA.
- Iniciar um projeto tende a ser muito simples com o monolito - é necessário planejar e arquitetar um ecossistema com menos camadas e componentes.
- O monitoramento de uma única aplicação é mais simples.

Com o passar dos ciclos de desenvolvimento, essas aplicações monolíticas tendem a crescer tanto em quantidade de linhas de código quanto em consumo de hardware, o que acarreta em:

- As chances de se quebrar a aplicação ao realizar alterações pontuais passam a ser cada vez maiores; 
- Quanto maior a aplicação, maior o número de testes unitários, o que exige um maior tempo de build sempre que se precisa compilar a aplicação;
- Um maior tempo de build provoca um processo de entrega mais longo;
- É comum que a área de negócio queira realizar testes de validação da aplicação ao liberar uma nova versão. Esses testes também requerem mais esforço, uma vez que - apesar de uma porção pequena ter sido alterada - toda a aplicação foi atualizada. Desta forma, os testes de homologação se tornam mais demorados;
- Evolução e migração dos dados requerem um maior planejamento prévio, uma vez que alterações em banco podem parar o funcionamento de toda a aplicação;
- A aplicação naturalmente passa a ocupar e consumir maior espaço em memória e possuir um maior tempo de start-up.
- Mudanças e entregas críticas exigem grande mobilização na organização e planejamento prévio para disponibilização em produção.

Notamos um dos maiores impasses: o acoplamento, em termos de código e de deploy. Quando a aplicação começa a se tornar muito complexa, com vários times de desenvolvimento e muitas funcionalidades, as adições ou alterações começam a se tornar cada vez mais custosas. Escalar um ambiente desses é desafiador. 

É aqui que entra o conceito dos microsserviços, que começa a desacoplar esses serviços e dar responsabilidades únicas para os serviços. Nesta abordagem você pode alterar, disponibilizar e escalar de maneira independente todo o ecossistema, seguindo sempre a premissa de não afetar os outros microsserviços.

## Microsserviços

A arquitetura orientada a microsserviços traz como preceito a criação de aplicações desacopladas entre si e modeladas conforme o domínio de negócios. Essas aplicações se integram através de diferentes protocolos, e os diversos padrões de comunicação (REST, GRPC, eventos assíncronos, entre outros) podem ser adotados. Com a adoção de uma arquitetura orientada a microsserviços, é possível promover entregas mais velozes e frequentes, além de trazer a quem desenvolve um ecossistema agnóstico de linguagem. 

![](images/chapter_07_02.png)

Como apontado por Sam Newman, em seu livro "Building Microservices", estes são conceitos que estão implícitos em microsserviços:

* São modelados levando-se em consideração o domínio do negócio;
* São altamente monitoráveis;
* Seu deploy pode ser feito de maneira independente dos outros serviços;
* Possui isolamento às falhas no ecossistema;
* Detalhes de implementação são "escondidos";
* Automação é essencial em todos os níveis.

Com essas características, alcançamos uma arquitetura flexível e escalável. 

> **TIP**: Tenha em mente que as vantagens expostas não necessariamente nos levam a um mundo onde as arquiteturas monolíticas não têm espaço. Na verdade, nós temos agora mais uma ferramenta em nossa caixa de ferramentas, uma forma diferente de entregar aplicações - que não necessariamente é a melhor forma para todos os cenários.

Vejamos algumas vantagens da utilização da abordagem de uma arquitetura orientada a microsserviços:

- **Escalabilidade vertical** - Devido à independência dos serviços, eles podem ser escalados conforme a necessidade, sem causar impactos nos demais serviços. Por serem menores, também requerem menos recursos de hardware.
- **Liberdade de selecionar tecnologias por projeto**: É possível escolher a melhor tecnologia para resolver determinado problema, mesmo que seja diferente das tecnologias utilizadas nos demais serviços.
- **Produtividade** - Esta abordagem suporta a existência de múltiplos times multidisciplinares (também conhecidos como squads) que podem atuar com um foco mais específico de negócio e, devido à independência técnica, entregar com maior velocidade. Em times grandes, a produtividade tende a tornar mais fáceis e menos problemáticas atividades como merge/rebase de um projeto se cada time trabalhar no seu próprio repositório. É muito importante pensar na organização e suas estruturas, que se refletem diretamente nos serviços e nas suas integrações. É o caso muito comum de que a estrutura do time impacta o modo como os softwares são organizados, muito bem explicado no Conways's law.
- **Agilidade** - Metodologias ágeis e suas diversas ramificações têm se mostrado cada vez mais populares. Ao se aliar esse estilo de gerenciamento de projeto com a arquitetura em microsserviços, permitimos tecnicamente a existência de ciclos completos e mais curtos para entrega de valor.
- **Reusabilidade** - Os componentes, como por exemplo um serviço que processe a lógica de negócio, podem ser consumidos via APIs, quando necessário, evitando assim a duplicidade de código e impactos na manutenibilidade.

### Desafios da arquitetura de microsserviços

A comunicação entre componentes de uma aplicação monolítica ocorrem in-memory, ou seja, não possuem o overhead da **latência** existente na comunicação via rede, como ocorre no cenário de microsserviços. Quanto mais o número de serviços e a complexidade arquitetural aumentam, mais catastrófico pode ser esse problema. Lidar com o tempo de resposta do serviço invocado no(a) cliente em si é uma boa prática (como configuração de timeouts e retries), assim como manter em dia os serviços de monitoramento e alertas da sua rede.

Atente-se à separação por responsabilidade dos componentes de back-end e de front-end. O **front-end** deve ser implementado de forma que, na falha de um dos serviços de back-end, os demais itens funcionem normalmente - garantindo a melhor experiência possível.

A arquitetura de microsserviços é agnóstica a linguagens e frameworks. Com isso, seu ambiente pode se tornar **poliglota**. Do ponto de vista arquitetural e de liderança técnica, tenha parcimônia na avaliação das tecnologias a serem utilizadas para que não se depare com um cenário onde há um serviço sem profissionais capacitados para mantê-lo. A definição do escopo e tamanho de um microsserviço deve ser mensurada. 

A definição do tamanho e escopo dos microsserviços pode ser uma tarefa que exija um pouco mais de esforço no início da jornada ao desacoplamento. Tenha em mente que o escopo e tamanho de um microsserviço devem ser mensurados a partir do princípio da responsabilidade única (**S**OLID). 

Um dos desafios de governança é evitar a existência de aplicações órfãs em ambiente produtivo. Procure estabelecer times responsáveis por cada serviço, inclusive em sua fase produtiva. Desta forma, caso ocorra um problema inesperado ou uma nova solicitação de mudança, será mais fácil identificar quem poderá assumir as tarefas.

Cuidado com a granualização demasiada de projetos por repositórios, essa opção pode não ser a melhor quando existem mais repositórios que colaboradores(as) na empresa.

## Migração de um monolito para microservices

Foram identificados padrões e técnicas que possibilitam a migração com sucesso de monolitos para arquitetura de microsserviços. Dois padrões bem populares para a realização dessa migração são [Strangler Fig Application (Estrangulamento)](https://martinfowler.com/bliki/StranglerFigApplication.html) e [UI Composition](https://www.thoughtworks.com/talks/a-high-performmance-solution-to-microservice-ui-composition). 

Caso você esteja em um cenário em que precisa migrar um monolito, é recomendado o estudo das estratégias presentes no livro [Monolith to Microservices, Sam Newman](https://samnewman.io/books/monolith-to-microservices/). Nesse livro, no capítulo 1, você também pode ver na prática um exemplo de migração de uma aplicação monolítica com o suporte do DDD. 

## Os erros mais comuns com microservices 

É muito comum existir a lista dos erros que todo software tem, principalmente quando há uma mudança de paradigma. Por exemplo, quando houve a migração para os bancos de dados NoSQL, certamente, o erro foi pensar em relacionamento em bancos de dados que não têm suporte a relacionamento, como [Cassandra](http://cassandra.apache.org/). A lista a seguir apresenta os erros mais comuns que encontramos nos microservices:

- Quebra de domínio: o [DDD](https://www.infoq.com/minibooks/domain-driven-design-quickly/) trouxe vários benefícios, principalmente o de trazer o código para próximo do negócio com a linguagem ubíqua. Dentro do DDD temos o conceito de domínios, e quando movemos para microservices é muito normal quebrar de maneira errada o negócio no domínio. Esse tipo acontece, principalmente, quando fazemos a quebra de maneira precoce. É o mesmo caso da modelagem no banco de dados que fazemos, justamente, quando não temos muita informação do negócio. [Em seu artigo de definição de domínio, Martin Fowler](https://martinfowler.com/bliki/BoundedContext.html) menciona o *bounded context*. Podemos ver isso num e-commerce, quando separamos o controle de estoque do produto; porém, o que acontece se a regra obrigar que o produto só possa ser exibido se tiver no estoque? Exato, toda vez que se consultar um produto, também precisará ser consultado o serviço de estoque, resultando num total acoplamento entre os dois serviços. Em outras palavras, o serviço de produto e o de estoque não deveriam estar em dois serviços nesse contexto.
- Não automatizar: uma das boas práticas existentes quando falamos de microservices, certamente, é o [CI/CD](https://www.infoworld.com/article/3271126/what-is-cicd-continuous-integration-and-continuous-delivery-explained.html). Essas técnicas são realmente importantes, uma vez que existe uma grande quantidade de máquinas a serem gerenciadas.
- Diversidade de linguagens: essa decisão é uma das mais intrigantes. Até o momento, não conheço um único projeto cujo objetivo seja exibir alguma coisa no console, porém é muito comum ouvir de grandes nomes recomendações baseadas num “Hello World”. É importante ter muito cuidado com esse tipo de decisão; afinal, quanto maior o número de linguagens dentro de uma empresa, mais e diversos campos o time terá que conhecer, ou existirão silos de conhecimento. Há diversas histórias que relatam que um sistema foi reescrito, simplesmente, por não ter um time técnico para manter ou porque a linguagem/framework foi descontinuada.
- [Sua aplicação não é grande suficiente para se tornar microservice](https://medium.com/swlh/stop-you-dont-need-microservices-dc732d70b3e0): nem toda aplicação grande precisará ser migrada ou criada com o objetivo de se tornar um ambiente de microsserviço. Um exemplo disso são as aplicações legadas e que atendem à necessidade de clientes.
- Um dos grandes argumentos para escolher microservices está na possibilidade de escolher escalar um componente individualmente. Porém, eis que surge a seguinte pergunta: realmente faz sentido escalar individualmente um componente?
- Microsserviços precisam de informações e, como todo banco de dados distribuído, eles enfrentam a teoria do [CAP](https://en.wikipedia.org/wiki/CAP_theorem). Dado um cenário no qual se realizam múltiplas atualizações em diversos serviços, é comum acrescentar um novo item na arquitetura: o padrão [SAGA](https://dzone.com/articles/microservices-using-saga-pattern), resultando numa maior complexidade e pontos de testes no seu ambiente.
- [Começar o projeto já como microservices tende a ser um grande erro](https://www.oreilly.com/content/should-i-use-microservices/), principalmente na instabilidade na definição dos domínios. Um erro na quebra dos serviços faz com que exista uma grande dependência e acoplamento entre eles. Considere um contexto em que os dados a serem utilizados estão armazenados em múltiplas bases de dados. Com pragmatismo, esse problema poderia ser facilmente resolvido em uma arquitetura monolítica, um join num banco de dados relacional, como MySQL ou PostgreSQL, ou um subdocumento num banco de dados NoSQL, como MongoDB.

* Utilizar microservices apenas porque grandes empresas utilizam esse tipo de arquitetura. No mundo de arquitetura de software, uma decisão não deve ser tomada apenas pela popularidade na solução. Como [Edson Yanaga](https://twitter.com/yanaga) fala em seu [livro](https://developers.redhat.com/books/migrating-microservice-databases-relational-monolith-distributed-data/): “Certamente, sempre lemos grandes coisas sobre as arquiteturas de microservices implementadas por empresas como Netflix ou Amazon. Então, deixe-me fazer uma pergunta: quantas empresas no mundo podem ser Netflix e Amazon?”.

## Conclusão

Como visto, a arquitetura de microsserviços traz muitos benefícios para o seu ambiente e lhe oferece a vantagem de deixar o desenvolvimento independente quando se tem vários times e funcionalidades, e essa independência se estende também para o deploy da aplicação. Ou seja, você dá velocidade e agilidade para seus times e consegue ter código de melhor qualidade, já que ele vai estar organizado ao redor da funcionalidade. Tem-se a vantagem de ser fácil de escalar apenas no ponto em que se precisa, e ainda poder ser aplicado na tecnologia que você tem mais domínio.

Mas, como já dito anteriormente, não é nenhuma bala de prata; há complexidades para o ambiente e novas preocupações em termos de segurança. Imagine um projeto gigante, com múltiplas instâncias e centenas de microsserviços; como você irá monitorar? Em caso de erro, como você vai encontrar, desviar ou mesmo tratar o erro?

Se usado da maneira correta, e tratados de perto os pontos de atenção, esse padrão de arquitetura tem muito a agregar nos seus projetos.

Microsserviços, como toda decisão de arquitetura, têm suas vantagens e desvantagens. Como diz Martin Fowler:  

> Os microservices introduzem eventuais problemas de consistência, por causa de sua louvável insistência no gerenciamento de dados descentralizado. Com um monolito, podemos atualizar várias coisas juntas em uma única transação. Os microservices exigem vários recursos para atualizar, e as transações distribuídas são desaprovadas (por um bom motivo). Portanto, agora, os desenvolvedores precisam estar cientes dos problemas de consistência e descobrir como detectar quando as coisas estão fora de sincronia antes de fazer qualquer coisa de que o código se arrependa. - [Martin Fowler](https://martinfowler.com/articles/microservice-trade-offs.html#consistency)

================================================
FILE: manuscript/chapter_08.md
================================================
# Cloud {#chapter_08}

Uma vez que conversamos sobre DDD, microservices, boas práticas, design de código e arquitetura de software, temos embasamento para prosseguir e abordar um dos temas mais discutidos do cenário de tecnologia: computação em nuvem (*cloud* *computing*).

Discorreremos sobre o que se considerar ao transpor arquitetura de aplicações para um ambiente de cloud, perspectivas populares a respeito de aplicações "cloud-native" e por que esse conceito é tão ligado a ferramentas como Kubernetes.  Serão também descritos padrões e funcionalidades esperadas em uma aplicação, para que sejam first-class citizens em um ambiente de cloud. 

>  **INFO:** Este capítulo não tem como meta ensinar a fazer deploy de serviços em um cluster Kubernetes, configurar serviços na AWS ou criar aplicações cloud-native from scratch. A intenção deste capítulo é prover informações arquiteturais que embasarão suas decisões e modelagem dos seus serviços e plataformas. Com o conhecimento aqui fornecido, você estará preparado(a) e confiante para iniciar ou prosseguir sua jornada cloud-native, independentemente da solução ou linguagem adotada. 

A buzz-word "cloud-native" começou a se estabelecer por volta de 2014, e sua crescente popularidade se mostra em seu melhor cenário. Para atingir maior espaço no mercado, empresas passaram a rotular seus produtos como cloud-native, quando, na verdade, são apenas tecnologias cloud-enabled. 

> **TIP:** Uma das formas de se avaliar a popularidade de um termo é validar a quantidade de buscas realizadas ao longo do tempo e as regiões interessadas.
>
> <https://trends.google.com.br/trends/explore?date=all&q=cloud-native>

O entendimento desses conceitos te auxiliará no entendimento da situação em que suas aplicações estão e dará suporte ao planejamento de uma jornada para a cloud. Vamos discorrer sobre essas categorizações, seus conceitos e diferenças.

## Cloud-Native ou Cloud-Enabled?

Uma aplicação pode ser categorizada conforme seu nível de adequação a um ambiente de cloud como sendo *cloud-enabled* ou *cloud-native* (a.k.a. *cloud-ready*).  

### Cloud-Enabled

É uma aplicação que foi conteinerizada e roda na cloud, mas que originalmente foi criada para rodar em ambiente tradicional, como por exemplo um data-center local, máquinas virtuais com cluster de servidores de aplicação tradicional. Essas aplicações podem ser categorizadas como *cloud-enabled* e têm maior consumo de recursos (cpu, memória, storage), em comparação a aplicações cloud-native. 

Uma aplicação cloud-enabled passou por refatorações e ajustes para rodar em ambiente conteinerizado e também para suportar orquestração por plataformas como Kubernetes. Afinal de contas, não é mais o tradicional cluster de WildFly (a.k.a. JBoss EAP) ou GlassFish (Weblogic) clusterizados, que permitem que você use a rede ou sistema de arquivos a seu bel-prazer. Agora, esses serviços rodam em pods, em contêineres efêmeros. 

Apesar dos "contras" de se possuir uma aplicação cloud-enabled, o custo ou esforço de se refatorar toda a aplicação não são viáveis. Desta forma, a aplicação pode rodar em cloud, mas não pode usufruir de todos os benefícios existentes em um ambiente de cloud. 

> **INFO:** [Kubernetes](https://kubernetes.io/): É uma ferramenta open-source de orquestração de containers e trabalha muito bem com o [Docker](https://www.docker.com/). Atualmente, é a ferramenta mais popular na comunidade. Outros exemplos de ferramenta de orquestração de containers são Docker Swarm, Mesos e Amazon ECS.

Para entender melhor tudo o que uma aplicação cloud-enabled *não* é capaz de utilizar nativamente, vamos falar sobre o conceito cloud-native.

### Perspectivas sobre o conceito cloud-native

No momento da escrita deste livro, não há um consenso ou
Download .txt
gitextract_6f6qi3n6/

├── .gitignore
├── LICENSE
├── README.md
└── manuscript/
    ├── Book.txt
    ├── Subset.txt
    ├── about_me.md
    ├── apendix-a.md
    ├── bibliography.md
    ├── chapter_01.md
    ├── chapter_02.md
    ├── chapter_03.md
    ├── chapter_04.md
    ├── chapter_05.md
    ├── chapter_06.md
    ├── chapter_07.md
    ├── chapter_08.md
    ├── chapter_09.md
    ├── chapter_10.md
    └── resources/
        └── readme.md
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (288K chars).
[
  {
    "path": ".gitignore",
    "chars": 7,
    "preview": ".vscode"
  },
  {
    "path": "LICENSE",
    "chars": 19337,
    "preview": "Attribution-NonCommercial 4.0 International\n\n=======================================================================\n\nCr"
  },
  {
    "path": "README.md",
    "chars": 2783,
    "preview": "# Manual do Arquiteto Moderno\n\n## Objetivo\n\nNeste livro você irá aprender sobre conceitos que fazem parte da vida de pro"
  },
  {
    "path": "manuscript/Book.txt",
    "chars": 181,
    "preview": "about_me.md\nchapter_01.md\nchapter_02.md\nchapter_03.md\nchapter_04.md\nchapter_05.md\nchapter_06.md\nchapter_07.md\nchapter_08"
  },
  {
    "path": "manuscript/Subset.txt",
    "chars": 11,
    "preview": "about_me.md"
  },
  {
    "path": "manuscript/about_me.md",
    "chars": 8255,
    "preview": "{width=60%}\n![](images/autores__otavio.png)\n\n* **Nome**: Otavio Santana\n\n* **Cargo**: Staff Engineer\n\n* **Bio:** Capacit"
  },
  {
    "path": "manuscript/apendix-a.md",
    "chars": 7981,
    "preview": "# Apêndice A: Segurança {#apendice_a}\n\n## Práticas de Segurança\n\nCódigo seguro deveria ser uma regra em qualquer design "
  },
  {
    "path": "manuscript/bibliography.md",
    "chars": 3529,
    "preview": "# Bibliografia\n\n\n\n- [Java Efetivo](https://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/ref=pd_lpo_14_t_"
  },
  {
    "path": "manuscript/chapter_01.md",
    "chars": 16800,
    "preview": "# A importância de aprender conceitos ao invés de novos frameworks {#chapter_01}\n\nEscrever este capítulo pode não ser um"
  },
  {
    "path": "manuscript/chapter_02.md",
    "chars": 58041,
    "preview": "# Tenho lido sobre DDD, para onde devo ir depois? {#chapter_02}\n\nVocê já codifica Java há muitos anos, já leu sobre Doma"
  },
  {
    "path": "manuscript/chapter_03.md",
    "chars": 20405,
    "preview": "# Clean code {#chapter_03}\n\nSão claros e reconhecidos os grandes benefícios obtidos por meio da utilização de boas práti"
  },
  {
    "path": "manuscript/chapter_04.md",
    "chars": 7640,
    "preview": "# Clean Architecture {#chapter_04}\n\nÉ interessante como os conceitos de Clean Architecture podem ser relacionados em div"
  },
  {
    "path": "manuscript/chapter_05.md",
    "chars": 12559,
    "preview": "# Refatoração {#chapter_05}\n\nApós alguns anos de estrada, quem desenvolve percebe que passa grande parte do seu tempo le"
  },
  {
    "path": "manuscript/chapter_06.md",
    "chars": 19264,
    "preview": "# NoSQL vs. SQL {#chapter_06}\n\nMuitas das vezes, quando iniciamos um debate sobre SQL (Structured Query Language) e NoSQ"
  },
  {
    "path": "manuscript/chapter_07.md",
    "chars": 17391,
    "preview": "# Arquitetura de microsserviços {#chapter_07}\n\nCom a popularização da utilização de ambientes de cloud para entrega de s"
  },
  {
    "path": "manuscript/chapter_08.md",
    "chars": 52925,
    "preview": "# Cloud {#chapter_08}\n\nUma vez que conversamos sobre DDD, microservices, boas práticas, design de código e arquitetura d"
  },
  {
    "path": "manuscript/chapter_09.md",
    "chars": 15368,
    "preview": "# Precisamos falar sobre atualizações {#chapter_09}\n\nNão vou mentir pra você: quando ouvi falar que o Java seria atualiz"
  },
  {
    "path": "manuscript/chapter_10.md",
    "chars": 14509,
    "preview": "# Destrinchando performance de aplicações {#chapter_10}\n\n## Introdução e conceitos\n\nMuitas empresas utilizam a abordagem"
  },
  {
    "path": "manuscript/resources/readme.md",
    "chars": 0,
    "preview": ""
  }
]

About this extraction

This page contains the full source code of the otaviojava/manual-arquiteto-moderno GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (270.5 KB), approximately 61.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!