[
  {
    "path": ".gitignore",
    "content": ".vscode"
  },
  {
    "path": "LICENSE",
    "content": "Attribution-NonCommercial 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n\twiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More_considerations\n     for the public: \n\twiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution-NonCommercial 4.0 International Public\nLicense\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution-NonCommercial 4.0 International Public License (\"Public\nLicense\"). To the extent this Public License may be interpreted as a\ncontract, You are granted the Licensed Rights in consideration of Your\nacceptance of these terms and conditions, and the Licensor grants You\nsuch rights in consideration of benefits the Licensor receives from\nmaking the Licensed Material available under these terms and\nconditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n  d. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  e. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  f. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  g. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  h. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  i. NonCommercial means not primarily intended for or directed towards\n     commercial advantage or monetary compensation. For purposes of\n     this Public License, the exchange of the Licensed Material for\n     other material subject to Copyright and Similar Rights by digital\n     file-sharing or similar means is NonCommercial provided there is\n     no payment of monetary compensation in connection with the\n     exchange.\n\n  j. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  k. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  l. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part, for NonCommercial purposes only; and\n\n            b. produce, reproduce, and Share Adapted Material for\n               NonCommercial purposes only.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties, including when\n          the Licensed Material is used other than for NonCommercial\n          purposes.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n       4. If You Share Adapted Material You produce, the Adapter's\n          License You apply must not prevent recipients of the Adapted\n          Material from complying with this Public License.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database for NonCommercial purposes\n     only;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material; and\n\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n=======================================================================\n\nCreative Commons is not a party to its public\nlicenses. Notwithstanding, Creative Commons may elect to apply one of\nits public licenses to material it publishes and in those instances\nwill be considered the “Licensor.” The text of the Creative Commons\npublic licenses is dedicated to the public domain under the CC0 Public\nDomain Dedication. Except for the limited purpose of indicating that\nmaterial is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the\npublic licenses.\n\nCreative Commons may be contacted at creativecommons.org.\n"
  },
  {
    "path": "README.md",
    "content": "# Manual do Arquiteto Moderno\n\n## Objetivo\n\nNeste 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. \n\nAlinhando 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. \n\n## O que você encontra no livro\n\nEste 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:\n\n    1. A importância de aprender os conceitos ao invés de novos frameworks;\n    2. Domain-Driven Design (DDD), where to go next?\n    3. Clean Code;\n    4. Clean Architecture;\n    5. Refatoração;\n    6. Banco de dados;\n    7. Microservices;\n    8. Cloud;\n    9. Precisamos falar sobre atualizações;\n    10. Destrinchando performance de aplicações;\n\n## O que você não vai encontrar neste livro\n\n* Resumo do contéudo de livros mencionados, listados na bibliografia;\n* Explicação de conceitos considerados básicos, como \"O que é SOLID\", \"Como se conectar a um banco de dados\". \n* Tutoriais e guias passo a passo. \n\n\n## Contribuições\n\nEste 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. \n\nO 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.\n\nCom 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\n\n\n\n<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>.\n"
  },
  {
    "path": "manuscript/Book.txt",
    "content": "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.md\nchapter_09.md\nchapter_10.md\napendix-a.md\nbibliography.md\n"
  },
  {
    "path": "manuscript/Subset.txt",
    "content": "about_me.md"
  },
  {
    "path": "manuscript/about_me.md",
    "content": "{width=60%}\n![](images/autores__otavio.png)\n\n* **Nome**: Otavio Santana\n\n* **Cargo**: Staff Engineer\n\n* **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.\n\n  {pagebreak}\n  \n  {width=60%}\n  ![](images/autores__sergio_elder.png)\n  \n* **Nome**: Sérgio Lopes\n\n* **Cargo**: Especialista em TI no Banco Itaú S/A\n\n* **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++.\n  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.\n  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.\n  \n  {pagebreak}\n  \n  {width=60%}\n  ![](images/autores__karina.png)\n  \n* **Nome:** Karina Varela\n* **Cargo:** Gerente Técnico Principal de Marketing de Produtos, Red Hat\n* **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.\nA 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.\n\n{pagebreak}\n\n{width=60%}\n![](images/autores__salaboy.png)\n\n* **Nome:** Mauricio Salatino (Salaboy)\n* **Cargo:** Engenheiro de Software\n* **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). \n\n{pagebreak}\n\n{width=60%}\n![](images/autores__elder-moraes.png)\n\n* **Nome:** Elder Moraes\n* **Cargo:** Developer Advocate, Red Hat\n* **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.\n\n{pagebreak}\n\n{width=60%}\n![](images/autores__sandro.png)\n\n* **Nome:** Sandro Giacomozzi\n* **Cargo:** Engenheiro de Software, TOTVS\n* **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.\n\n{pagebreak}\n\n{width=60%}\n![](images/autores__isidro.png)\n\n* **Nome:** Francisco Isidro\n\n* **Cargo:** Professor, Pesquisador da Universidade Federal do ABC\n* **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. \n\n{width=60%}\n![](images/autores__leandro.png)\n\n* **Name:** Leandro Domingues\n* **Job Title:** Fundador da Cluster Consultoria, MongoDB Champion / Consulting Engineer, Microsoft Data Platform MVP, Community Manager, Speaker\n* **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.\n* **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. \n\n  {pagebreak}\n  ![](images/insercao_1.jpg)\n"
  },
  {
    "path": "manuscript/apendix-a.md",
    "content": "# 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 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.\n\nSé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. \n\nNo caso de Microsserviços, a abordagem que muda é em relação a Autorização e Autenticação, e vamos discutir isso mais à frente.\n\nUma 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.\n\nE 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.\n\nOutra 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.\n\nE 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.\n\nComo 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.\n\nA chave é seguir ao menos cinco pilares:\n\n- Segurança por design (Secure by design);\n- Vasculhe suas dependências;\n- Use sempre HTTPS;\n- Use Tokens de Acesso e Identidade;\n- Proteja e criptografe seus segredos.\n\n#### Soluções para Autenticação e Autorização\n\nPara 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.\n\nAutenticaçã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.\n\nO 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.\n\nE outro ponto que é preciso analisar é que os microsserviços devem ser *stateless*, então é necessário usar soluções que consigam manter isso.\n\nPodemos 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:\n\n**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.\n\n**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.\n\n**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.\n\nMas 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.\n\nO 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/).\n\nE 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.\n\nCom 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. \n\nE 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.\n\nO 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:\n\n**Resource Owner** - Esse é o papel que controla os acessos aos recursos.\n\n**Resource Server** - É onde ficam os serviços a serem acessados, ou seja, aqui é onde estão as suas API's, aplicações e etc.\n\n**Client** - É quem faz a solicitação ao Resource Owner do recurso que precisa consumir.\n\n**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.\n\nE 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.\n\nE 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.\n\nTirando 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."
  },
  {
    "path": "manuscript/bibliography.md",
    "content": "# Bibliografia\n\n\n\n- [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);   \n- [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);\n- [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): \n  - [Implementing DDD](https://www.amazon.co.uk/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577) \n  - [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)\n  - [The Business Value of Using DDD](https://www.informit.com/articles/article.aspx?p=1944876&seqNum=4)\n  - [Domain Driven Design Quickly](https://www.infoq.com/minibooks/domain-driven-design-quickly/)\n- [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);\n- [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);\n- [The Twelve Factors](https://12factor.net/);\n- [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)\n- [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)\n- [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\n- [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ß\n- [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\n- [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"
  },
  {
    "path": "manuscript/chapter_01.md",
    "content": "# A importância de aprender conceitos ao invés de novos frameworks {#chapter_01}\n\nEscrever 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).\n\nPois 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. \n\nO 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.\n\n## Por que estudar Análise de Algoritmos, Estruturas de Dados, Sistemas Operacionais, Arquitetura de Computadores, Grafos, entre outros?\n\nPodemos 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:\n\nConsidere 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. \n\nTer 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:\n\n\trecuperar conjunto de pedidos e armazenar em listaP\n\tpara cada pedido P em listaP faça\n\t\trecuperar itens I[] do pedido P a partir de seu ID\n\tfim-para\n\n{pagebreak}\n\nDe forma bastante geral, a execução das instruções pode ser descrita da forma abaixo:\n\n\t1 execução da linha 1\n\tN+1 execuções da linha 2 (for sempre tem o teste que retorna falso)\n\tN execuções da linha 3\n\nDesse 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.\n\nPodemos 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:\n\n\trecuperar conjunto de pedidos e armazenar em listaP\n\tpara cada pedido P em listaP[] faça\n\t\trecuperar itens I[] do pedido P a partir de seu ID\n\t  para cada item I de P, faça\n\t\trecuperar lista L de impostos de I[]\n\t  fim-para\n\tfim-para\n\nNesse caso, analisando a complexidade assintótica, temos:\n\n\t1 execução da linha 1\n\tN+1 execuções da linha 2\n\tN execuções das linhas de 3 a 5, o que implica:\n\t\t1 execução da linha 3\n\t\tN+1 execuções da linha 4\n\t\tN execuções da linha 5\n\nPortanto, 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. \nIsso 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?\n\nA 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.\n\n## A importância de um estudo mais aprofundado em disciplinas teóricas\nSabemos 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.\n\nQuer exemplos? \n\n- **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.\n- **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. \n\nAlé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:\n\n```java\n\tArrayList<Produto> lista;\n\tlista = new ArrayList<Produto>();\n\tfor (int i = 0; i < 100000; i++) {\n\t    Produto p = new Produto(i + 1, \"Produto \" + (i + 1), 0, 0);\n\t    lista.add(p);\n\t}\n\tint quantosAchei=0; // numero de ocorrencias encontradas\n\t// inicio da medicao do tempo\n\tlong inicio = System.currentTimeMillis();\n\tProduto busca;\n\tfor (int cont = 0; cont < 10000; cont++) {\n\t    for (int i = 0; i < lista.size(); i++) {\n\t        Produto tmp = lista.get(i);\n\t        if (tmp.getId() == -1) { // forcando buscar um ID que nao existe na lista -> o pior caso\n\t             busca = tmp;\n\t             quantosAchei++;\n\t             break;\n\t         }\n\t     }\n\t }\n\t long fim = System.currentTimeMillis();\n\t // fim da medicao do tempo\n\t System.out.println(\"Achei = \" + quantosAchei +\" em \"+(fim-inicio));\n```\n\nEsse 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).\nÉ 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. \n\nAgora, se pensarmos em outra estrutura, como um mapa Hash, qual a vantagem? Vamos observar este código.\n\n\t\tHashMap<Integer, Produto> mapa;\n\t\tmapa = new HashMap<Integer, Produto>();\n\t\tfor (int i = 0; i < 1000000; i++) {\n\t\t   Produto p = new Produto(i + 1, \"Produto \" + (i + 1), 0, 0);\n\t\t   mapa.put(p.getId(), p);\n\t\t}\n\t\tint quantosAchei=0;\n\t\t\n\t\t// inicio da medicao\n\t\tlong inicio = System.currentTimeMillis();\n\t\tfor (int cont=0; cont< 10000; cont++) {\n\t\t\tProduto busca = mapa.get(-1); // novamente forcando a busca de um elemento que nao existe\n\t\t\tif (busca != null) {\n\t\t\t\tquantosAchei++;\n\t\t\t}\n\t\t}\n\t\tlong fim = System.currentTimeMillis();\n\t\t// fim da medicao\n\t\tSystem.out.println(\"Achei = \"+quantosAchei+\" em \"+(fim-inicio));\n\nPela 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!\n\nDesse 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\nNã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.\n\nAgora, 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?\n\nUm 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:\n\n**Fragmento 1**: preenchimento Linha x Coluna\n\n\n\tfor (int i=0; i < TAM; i++)\n\t\tfor (int j=0; j < TAM; j++)\n\t\t\tmatriz[i][j] = valor;\n\n**Fragmento 2**: preenchimento Coluna x Linha\n\n```\nfor (int i=0; i < TAM; i++)\n\tfor (int j=0; j < TAM ; j++)\n\t\tmatriz[j][i] = valor;\n```\n\nO 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).\n\nMas 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.\n\nAliá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.\n\nE 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\nNã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).\n\nMas 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. \n\n## O ponto-chave: usar frameworks, fazer as tarefas manualmente, ou melhor, criar seu próprio?\n\nDifí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.\n\n**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.\n\n**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.\n\n**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).\n\nPortanto, 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*\"."
  },
  {
    "path": "manuscript/chapter_02.md",
    "content": "# Tenho lido sobre DDD, para onde devo ir depois? {#chapter_02}\n\nVocê 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?\n\nO 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.\n\nEste 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.\n\n> **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.\n\n{pagebreak}\n\nEste capítulo está dividido em duas seções principais:\n\n- [Introdução aos tópicos relacionados a Java e nuvem](#java-na-nuvem)\n- [De Monolith até K8s usando DDD](#evoluindo-seu-monolito)\n\n\n## Java na nuvem {#java-na-nuvem}\n\nExistem 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.\n\nIsso 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.\n\nEnquanto 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:\n\n- 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.\n- Cada serviço é construído, mantido, desenvolvido e implantado por uma equipe diferente.\n- Cada serviço tem seu próprio ciclo de lançamento.\n- Cada serviço expõe um conjunto bem definido de APIs.\n\nConstruir 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.\n\n> **DICA:** Para mais detalhes sobre a arquitetura de microsserviços, consulte o capítulo [Microsserviços](#chapter_07).\n\nO 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.\n\n\n### Containers & organização de containers\n\nSeguindo 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.\n\nDocker (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.\n\nCada container Docker tem seu próprio tempo de execução isolado, o que nos permite isolar as falhas nos limites do container Docker.\n\nQuando 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.\n\nQuando o número de serviços aumenta, isso se torna incontrolável.\nPor 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.\n\n> **DICA:** Para obter mais detalhes sobre containers e ferramentas de orquestração, consulte o capítulo sobre [Cloud](#chapter_08).\n\nO 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.\n\n\n### Capitalizando os benefícios do DDD\n\nSe 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.\n\nEu 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.\n\nA 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.\n\n\n## Evoluindo seu monolito na prática, usando DDD {#evoluindo-seu-monolito}\n\nEsta 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.\n\nComo 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.\n\n- [Repositório De Monolith para K8s Github](https://github.com/salaboy/from-monolith-to-k8s)\n\n> **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.\n\nComeç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.\nTodos nós vimos grandes aplicações Java Web e, neste cenário, a aplicação se parece com isto:\n\n![Imagem 02_01: Aplicação monolítica para gerenciamento de conferências.](images/chapter_02_01a.png)\n\nO 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.\n\nComo 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.\n\n> 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.\n\nAgora, 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:\n\n- Clientes não podem ser escalados independentemente.\n- O tráfego de clientes é todo gerenciado pela mesma aplicação.\n- Ponto Único de Falha na JVM.\n- 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.\n- Cada mudança na plataforma requer que toda a aplicação seja reiniciada.\n- 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.\n\n> **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.\n\nComo 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.\n\nO próximo passo em nossa jornada é decidir por onde começar. Em minha experiência, vi três padrões comuns se repetindo:\n\n- **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.\n- **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.\n- **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).\n\nNeste 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.\n\n> **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).\n\n\nÉ 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.\n\n\n### Suposições e simplificações\n\nAs 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.\n\nPara 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.\n\nDo 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.\n\n> **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.\n\n{pagebreak}\n\nNossos sites de conferências independentes serão semelhantes a este:\n\n![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)\n\nComo 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).\n\nMas, 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.\n\n{pagebreak}\n\n### Contextos limitados para começar a dividir seu monolito\n\nSe 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ê.\nNa 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á.\n\nNo caso do nosso cenário, lidar com a Chamada de Propostas (Call for Proposals) é fundamental para iniciar uma conferência.\nHá um trabalho substancial para aceitar propostas, revisá-las e garantir que a conferência tenha uma agenda interessante.\n\nSe 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.\n\nO 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.\n\n> **INFO:** De uma perspectiva DDD, é extremamente importante não tornar esses Bounded Contexts um limite técnico que é imposto aos especialistas em domínio.\n\nUm 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.\n\nO 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.\n\nAssim 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:\n\n![Imagem 02_03: Bounded contexts da aplicação de conferências.](images/chapter_02_03.png)\n\nCada 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.\n\nDo 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.\n\nPara 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.\n\nAprofundando os detalhes práticos, existem algumas das melhores práticas compartilhadas por muitas empresas e ferramentas:\n\n- Um Repositório / Um Serviço + Entrega Contínua\n- APIs abertas\n\n\n#### Um Repositório / Um Serviço + Entrega Contínua\n\nNormalmente, é 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.\n\nHoje 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.\n\nCom 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.\n\nVocê, 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.\n\n![Imagem 02_04: Boas práticas em um processo deploy de aplicações no Kubernetes.](images/chapter_02_04.png)\n\nPara fazer o deploy do seu código no Kubernetes: \n\n- Você precisará construir e testar seu código-fonte. Se for Java, você pode usar, por exemplo, Maven ou Gradle para fazer isso;\n\n- 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).\n\n- A partir desse JAR, você cria uma imagem Docker definindo um Dockerfile que entende como executar sua aplicação Java com uma JVM fornecido.\n\n- Por fim, se quiser fazer o deploy do seu container para um cluster Kubernetes, você precisará de alguns manifestos YAML.\n\n- 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.\n\n  > **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.\n\nNeste 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.\n\n> **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.\n\nComo 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.\n\n> **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.\n\nUma 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.\n\n> **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/).\n\nNo 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.\n\n![Imagem 02_05: Com a prática proposta, um repositório representa um serviço que será implantado.](images/chapter_02_05.png)\n\nEm nosso exemplo, os links a seguir demonstram todos esses conceitos em ação.\n\n- [Pipeline](https://github.com/salaboy/fmtok8s-email/blob/master/jenkins-x.yml)\n- [DockerFile](https://github.com/salaboy/fmtok8s-email/blob/master/Dockerfile)\n- [Gráficos do Helm](https://github.com/salaboy/fmtok8s-email/tree/master/charts/fmtok8s-email)\n- [Versões contínuas](https://github.com/salaboy/fmtok8s-email/releases)\n\nVocê pode encontrar o mesmo setup para todos os projetos dentro da Demonstração do Site de Conferência.\n\n\n#### APIs abertas\nSe 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.\n\n> **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)\n\nConforme 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.\n\nApenas 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.\n\nSe você está usando Spring Boot padrão (Tomcat), será preciso adicionar ao seu arquivo `pom.xml`:\n\n```xml\n<dependency>\n   <groupId>org.springdoc</groupId>\n   <artifactId>springdoc-openapi-ui</artifactId>\n   <version>${springdoc-openapi-ui.version}</version>\n</dependency>\n```\nSe você estiver usando o Webflux, a pilha reativa que você precisa adicionar é:\n\n```xml\n<dependency>\n   <groupId>org.springdoc</groupId>\n   <artifactId>springdoc-openapi-webflux-ui</artifactId>\n   <version>${springdoc-openapi-ui.version}</version>\n</dependency>\n```\n\n- <https://github.com/salaboy/fmtok8s-email/blob/master/pom.xml#L40>\n\nEm 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.\n\nA 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.\n\n![Imagem 02_06: Interface da Swagger API](images/chapter_02_06.png)\n\nSinta-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>\n\n### Mapa de contexto para entender as interações técnicas e da equipe\n\nBounded 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.\n\nDo 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.\n\nComo 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.\n\nMapas 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.\n\nPara nosso exemplo, o seguinte mapa de contexto faria sentido:\n![Imagem 02_07: Context Map para o Call for Papers da aplicação de conferências](images/chapter_02_07.png)\n\nEsse 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.\n\nPor 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.\n\n> **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>.\n\nEmbora 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).\n\n#### Bônus: Implementação de contratos com Spring Cloud Contracts\n\nCom 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.\n\nUm contrato é semelhante a isto: <https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/test/resources/contracts/shouldAcceptPostProposal.groovy>\n\n```groovy\nContract.make {\n       name \"should accept POST with new Proposal\"\n       request{\n           method 'POST'\n           url '/'\n           body([\n               \"title\": $(anyNonEmptyString()),\n               \"description\": $(anyNonEmptyString()),\n               \"author\": $(anyNonEmptyString()),\n               \"email\": $(anyEmail())\n           ])\n           headers {\n               contentType('application/json')\n           }\n       }\n       response {\n           status OK()\n           headers {\n               contentType('application/json')\n           }\n           body(\n                   \"id\": $(anyUuid()),\n                   \"title\": $(anyNonEmptyString()),\n                   \"description\": $(anyNonEmptyString()),\n                   \"author\": $(anyNonEmptyString()),\n                   \"email\": $(anyEmail())\n           )\n       }\n   }\n```\nEste 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`.\n\nAgora, 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:\n<https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/pom.xml#L50>\n\n```xml\n<dependency>\n  <groupId>org.springframework.cloud</groupId>\n  <artifactId>spring-cloud-starter-contract-verifier</artifactId>\n  <version>${spring.cloud.contract}</version>\n  <scope>test</scope>\n</dependency>\n\n```\n\nE na seção `<build><plugins>`, o seguinte plugin:\n<https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/pom.xml#L88>\n\n```xml\n<plugin>\n  <groupId>org.springframework.cloud</groupId>\n  <artifactId>spring-cloud-contract-maven-plugin</artifactId>\n  <version>${spring.cloud.contract}</version>\n  <extensions>true</extensions>\n  <configuration>\n    <packageWithBaseClasses>com.salaboy.conferences.c4p</packageWithBaseClasses>\n    <testMode>EXPLICIT</testMode>\n   </configuration>\n</plugin>\n\n```\n\nFinalmente, 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>\n\n```java\n@RunWith(SpringRunner.class)\n@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,\n        properties = \"server.port=0\")\npublic abstract class ContractVerifierBase {\n\n    @LocalServerPort\n    int port;\n    \n    ...\n}\n```\n\nSe você precisar adicionar qualquer código de inicialização, esta é a classe que todo teste gerado automaticamente deve herdar.\n\nAgora 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.\n\nIsso 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.\nEsse 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.\n\nFinalmente, 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:\n\n\n```java\n@RunWith(SpringRunner.class)\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)\n@AutoConfigureMockMvc\n@AutoConfigureJsonTesters\n@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.REMOTE, repositoryRoot = \"<your nexus repository>\", ids = \"com.salaboy.conferences:fmtok8s-c4p\")\npublic class C4PApisTests {\n   @StubRunnerPort(\"fmtok8s-c4p\")\n    int producerPort;\n\n...\n}\n```\n\n<https://github.com/salaboy/fmtok8s-api-gateway/blob/master/src/test/java/com/salaboy/conferences/site/C4PApisTests.java#L29>\n\nIsso 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`. \n\nÉ 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.\n\n> **DICA:** O capítulo [Cloud](#chapter_08) cobre Canary Releases, bem como outras estratégias de implantação.\n\nAmbos, 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.\n\n\n### Foco no valor do negócio\n\nAo 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.\n\nPara 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:\n\n1. O palestrante em potencial envia uma proposta pelo site da conferência\n2. O Conselho/Comitê analisa a proposta\n3. Se a proposta for aceita\n   1. Um novo item da agenda é criado e publicado na página da agenda\n4. Uma notificação é enviada por e-mail para propostas aceitas e rejeitadas\n\nVocê 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.\n\n> **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).\n\nA interface de usuário que cobre esse cenário simples é assim:\n\n* 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ê.\n\n  ![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)\n\n  {pagebreak}\n\n* A página principal também permite que palestrantes em potencial enviem propostas, preenchendo um formulário:\n\n  ![Imagem 02_09: Formulário de Call for Papers](images/chapter_02_09.png)\n\n  {pagebreak}\n\n* 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:\n\n  ![Imagem 02_10: Página de aprovação acessada pelo comitê do evento](images/chapter_02_10.png)\n\n  {pagebreak}\n\n* 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.\n\n  ![Imagem 02_10: Página de utilizada pela organização para contactar potenciais palestrantes.](images/chapter_02_11.png)\n\n\n\nMais 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.\n\n{pagebreak}\n\n#### Arquitetura e Serviços\n\nDe uma perspectiva arquitetônica, parece mais assim:\n\n![Imagem 02_12: Arquitetura e usuários da aplicação de conferências.](images/chapter_01_12.png)\n\nA 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.\n\n### Gateway de API / Interface do usuário\nNa 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.\n\n> **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.\n\nO módulo API Gateway / interface do usuário pode ser encontrado neste repositório: <https://github.com/salaboy/fmtok8s-api-gateway>\n\nO que adiciona às suas dependências maven:\n\n```xml\n<dependency>\n  <groupId>org.springframework.cloud</groupId>\n  <artifactId>spring-cloud-starter-gateway</artifactId>\n</dependency>\n```\nE configure as rotas padrão para nossos serviços dentro do arquivo application.yaml:\n<https://github.com/salaboy/fmtok8s-api-gateway/blob/master/src/main/resources/application.yaml#L4>\n\n{language=yaml}\n\n```yaml\nspring:\n  cloud:\n    gateway:\n      routes:\n      - id: c4p\n        uri: ${C4P_SERVICE:http://fmtok8s-c4p}\n        predicates:\n        - Path=/c4p/**\n        filters:\n          - RewritePath=/c4p/(?<id>.*), /$\\{id}\n      - id: email\n        uri: ${EMAIL_SERVICE:http://fmtok8s-email}\n        predicates:\n        - Path=/email/**\n        filters:\n          - RewritePath=/email/(?<id>.*), /$\\{id}\n```\n\nEssas rotas definem um caminho para o gateway como `/c4p/**`, que irá encaminhar automaticamente a solicitação para o serviço <http://fmtok8s-c4p>.\n\n> **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>\n> 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>\n\nComo 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.\n\n### Eventos de domínio e o serviço Call for Proposals\nComo 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).\n\n> **INFO:** O Serviço Call for Proposals pode ser encontrado aqui: <https://github.com/salaboy/fmtok8s-c4p/>\n\nO 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.\n\n> **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>\n\nMais 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.\n<https://github.com/salaboy/fmtok8s-c4p/blob/no-workflow/src/main/java/com/salaboy/conferences/c4p/C4PController.java#L60>\n\n```java\n@PostMapping(value = \"/{id}/decision\")\n    public void decide(@PathVariable(\"id\") String id, @RequestBody ProposalDecision decision) {\n        emitEvent(\"> Proposal Approved Event ( \" + ((decision.isApproved()) ? \"Approved\" : \"Rejected\") + \")\");\n        Optional<Proposal> proposalOptional = proposalStorageService.getProposalById(id);\n        if (proposalOptional.isPresent()) {\n            Proposal proposal = proposalOptional.get();\n\n            // Apply Decision to Proposal\n            proposal.setApproved(decision.isApproved());\n            proposal.setStatus(ProposalStatus.DECIDED);\n            proposalStorageService.add(proposal);\n\n//          Only if it is Approved create a new Agenda Item into the Agenda Service\n            if (decision.isApproved()) {\n                agendaService.createAgendaItem(proposal);\n            }\n\n            // Notify Potential Speaker By Email\n            emailService.notifySpeakerByEmail(decision, proposal);\n        } else {\n            emitEvent(\" Proposal Not Found Event (\" + id + \")\");\n        }\n\n    }\n```\n\nComo 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.\n\nEste 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.\n\n### Armadilhas comuns\nAs 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.\n\nVamos começar com algo que você pode ter enfrentado no passado: comunicações REST para REST podem ser desafiadoras.\n\n#### Comunicações REST para REST podem ser desafiadoras\nSe 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á.\n\nEste 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.\n\nUma 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.\n\nOutra 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.\n\nFinalmente, 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.\n\n> **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).\n\n#### Fluxo enterrado no código\nÉ 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.\n\nO exemplo discutido neste capítulo se tornaria difícil de ler se adicionássemos o código para lidar com outros aspectos, como:\n\n- Caminhos infelizes e casos excepcionais: como o palestrante que apresentou a proposta desapareceu e não está respondendo a e-mails.\n- 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.\n- 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.\n- 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\nNã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.\n\nOs 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.\n\nAs 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.\n\n> **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.\n\nFinalmente, 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.\n\n#### Adaptadores (Adapters) para sistemas legados\n\nUma 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.\n\nO 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.\n\n> **INFO:** O código-fonte desse serviço pode ser encontrado aqui: <https://github.com/salaboy/fmtok8s-email>\n\nConsidere 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.\n\n## Resumindo\n\nEste 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:\n\n- 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.\n- 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.\n- 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.\n- 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."
  },
  {
    "path": "manuscript/chapter_03.md",
    "content": "# Clean code {#chapter_03}\n\nSã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.\n\n## Modelos Ricos\n\nLevando 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? \n\nVamos 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:\n\n- O nome do jogador, representado pelo atributo `name` da classe `Player`;\n- A posição (`position` da classe `Player`) do jogador (goleiro, ataque, defesa e meio de campo); \n- O ano em que o jogador entrou no time, representado pelo atributo `start` da classe `Player`; \n- O ano em que o jogador saiu do time, representado pelo atributo `end` da classe `Player`; \n- O número de gols que o jogador realizou no time, representado pelo atributo `goals` da classe `Player`;\n- O salário do jogador, representado pelo atributo `salary` da classe `Player`;\n- O email para contato, no atributo `email`;\n- A relação com o time, representada pela classe `Team`;\t\n- Lembrando a regra, _um time não deve ter mais que vinte membros_.\n\nCom base nas informações citadas, a primeira versão do modelo é mostrada a seguir:\n\n```java\nimport java.math.BigDecimal;\n\npublic class Player {\n\n    String name;\n\n    Integer start;\n\n    Integer end;\n\n    String email;\n\n    String position;\n\n    Integer goals;\n\n    BigDecimal salary;\n}\n\npublic class Team {\n\n    String name;\n\n    List<Player> players;\n}\n\n```\n\nÀ 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`:\n\n```java\npublic enum Position {\n    GOALKEEPER, DEFENDER, MIDFIELDER, FORWARD;\n}\n```\n\nUma 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. \n\nSob essa perspectiva, vamos analisar algumas regras de negócio da nossa aplicação de exemplo: \n\n- 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. \n- O ano de saída pode estar vazio, porém, quando preenchido, deverá ser posterior à data de entrada; \n- 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. \n\nVamos 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:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Team {\n\n    private static final int SIZE = 20;\n\n    private String name;\n\n    private List<Player> players = new ArrayList<>();\n\n    @Deprecated\n    Team() {\n    }\n\n    private Team(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void add(Player player) {\n        Objects.requireNonNull(player, \"player is required\");\n\n        if (players.size() == SIZE) {\n            throw new IllegalArgumentException(\"The team is full\");\n        }\n        this.players.add(player);\n    }\n\n    public List<Player> getPlayers() {\n        return Collections.unmodifiableList(players);\n    }\n\n    public static Team of(String name) {\n        return new Team(Objects.requireNonNull(name, \"name is required\"));\n    }\n}\n\n```\n\n> **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.\n\nCom 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.\n\n```java\nimport java.math.BigDecimal;\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic class Player {\n\n    private String name;\n\n    private Integer start;\n\n    private Integer end;\n\n    private String email;\n\n    private Position position;\n\n    private BigDecimal salary;\n\n    private int goal = 0;\n\n\n    public String getName() {\n        return name;\n    }\n\n    public Integer getStart() {\n        return start;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public Position getPosition() {\n        return position;\n    }\n\n    public BigDecimal getSalary() {\n        return salary;\n    }\n\n    public Optional<Integer> getEnd() {\n        return Optional.ofNullable(end);\n    }\n\n    public void setEnd(Integer end) {\n        if (end != null && end <= start) {\n            throw new IllegalArgumentException(\"the last year of a player must be equal or higher than the start.\");\n        }\n        this.end = end;\n    }\n}\n\n    public int getGoal() {\n        return goal;\n    }\n\npublic void goal() {\n       goal++;\n}\n\n```\n\nUma 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:\n\n- 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).\n- 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.\n\nPara resolver esses problemas, executaremos dois passos: utilização de tipos customizados e aplicação do padrão Builder.\n\n### Tipos customizados\n\nA 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:\n\n```java\nimport java.util.Objects;\nimport java.util.function.Supplier;\nimport java.util.regex.Pattern;\n\npublic final class Email implements Supplier<String> {\n\n    private static final String EMAIL_PATTERN =\n            \"^[_A-Za-z0-9-\\\\+]+(\\\\.[_A-Za-z0-9-]+)*@\"\n                    + \"[A-Za-z0-9-]+(\\\\.[A-Za-z0-9]+)*(\\\\.[A-Za-z]{2,})$\";\n\n    private static final Pattern PATTERN = Pattern.compile(EMAIL_PATTERN);\n\n    private final String value;\n\n    @Override\n    public String get() {\n        return value;\n    }\n\n    private Email(String value) {\n        this.value = value;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        Email email = (Email) o;\n        return Objects.equals(value, email.value);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hashCode(value);\n    }\n\n    @Override\n    public String toString() {\n        return value;\n    }\n\n    public static Email of(String value) {\n        Objects.requireNonNull(value, \"o valor é obrigatório\");\n        if (!PATTERN.matcher(value).matches()) {\n            throw new IllegalArgumentException(\"Email nao válido\");\n        }\n\n        return new Email(value);\n    }\n}\n\n```\n\nAgora iremos utilizar esses tipos na classe `Player`. Ela ficará com o seguinte formato:\n\n```java\nimport javax.money.MonetaryAmount;\nimport java.time.Year;\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic class Player {\n\n    private String id;\n\n    private String name;\n\n    private Year start;\n\n    private Year end;\n\n    private Email email;\n\n    private Position position;\n\n    private MonetaryAmount salary;\n\n//...\n\n}\n\n```\n\n### Builder Pattern \n\nO 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.\t \t \n\nUm 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`:\n\n```java\nimport javax.money.MonetaryAmount;\nimport java.time.Year;\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic class Player {\n\n    static final Year SOCCER_BORN = Year.of(1863);\n\n    //hide\n\n    private Player(String name, Year start, Year end, Email email, Position position, MonetaryAmount salary) {\n        this.name = name;\n        this.start = start;\n        this.end = end;\n        this.email = email;\n        this.position = position;\n        this.salary = salary;\n    }\n\n    @Deprecated\n    Player() {\n    }\n\n    public static PlayerBuilder builder() {\n        return new PlayerBuilder();\n    }\n\n    public static class PlayerBuilder {\n\n        private String name;\n\n        private Year start;\n\n        private Year end;\n\n        private Email email;\n\n        private Position position;\n\n        private MonetaryAmount salary;\n\n        private PlayerBuilder() {\n        }\n\n        public PlayerBuilder withName(String name) {\n            this.name = Objects.requireNonNull(name, \"name is required\");\n            return this;\n        }\n\n        public PlayerBuilder withStart(Year start) {\n            Objects.requireNonNull(start, \"start is required\");\n            if (Year.now().isBefore(start)) {\n                throw new IllegalArgumentException(\"you cannot start in the future\");\n            }\n            if (SOCCER_BORN.isAfter(start)) {\n                throw new IllegalArgumentException(\"Soccer was not born on this time\");\n            }\n            this.start = start;\n            return this;\n        }\n\n        public PlayerBuilder withEnd(Year end) {\n            Objects.requireNonNull(end, \"end is required\");\n\n            if (start != null && start.isAfter(end)) {\n                throw new IllegalArgumentException(\"the last year of a player must be equal or higher than the start.\");\n            }\n\n            if (SOCCER_BORN.isAfter(end)) {\n                throw new IllegalArgumentException(\"Soccer was not born on this time\");\n            }\n            this.end = end;\n            return this;\n        }\n\n        public PlayerBuilder withEmail(Email email) {\n            this.email = Objects.requireNonNull(email, \"email is required\");\n            return this;\n        }\n\n        public PlayerBuilder withPosition(Position position) {\n            this.position = Objects.requireNonNull(position, \"position is required\");\n            return this;\n        }\n\n        public PlayerBuilder withSalary(MonetaryAmount salary) {\n            Objects.requireNonNull(salary, \"salary is required\");\n            if (salary.isNegativeOrZero()) {\n                throw new IllegalArgumentException(\"A player needs to earn money to play; otherwise, it is illegal.\");\n            }\n            this.salary = salary;\n            return this;\n        }\n\n        public Player build() {\n            Objects.requireNonNull(name, \"name is required\");\n            Objects.requireNonNull(start, \"start is required\");\n            Objects.requireNonNull(email, \"email is required\");\n            Objects.requireNonNull(position, \"position is required\");\n            Objects.requireNonNull(salary, \"salary is required\");\n\n            return new Player(name, start, end, email, position, salary);\n        }\n\n    }\n}\n\n```\n\n{pagebreak}\n\nDessa forma, teremos a certeza de que, quando a instância de um jogador (`Player`) for criada, ela será consistente e à prova de falhas.\n\n```java\n     CurrencyUnit usd = Monetary.getCurrency(Locale.US);\n     MonetaryAmount salary = Money.of(1 _000_000, usd);\n     Email email = Email.of(\"marta@marta.com\");\n     Year start = Year.now();\n     Year end = start.plus(-1, ChronoUnit.YEARS);\n\n     Player marta = Player.builder().withName(\"Marta\")\n         .withEmail(email)\n         .withSalary(salary)\n         .withStart(start)\n         .withPosition(Position.FORWARD)\n         .build();\n\n```\n\nPodemos aplicar esse mesmo princípio na criação de um time (`Team`):\n\n```java\n  Team bahia = Team.of(\"Bahia\");\n  Player marta = Player.builder().withName(\"Marta\")\n      .withEmail(email)\n      .withSalary(salary)\n      .withStart(start)\n      .withPosition(Position.FORWARD)\n      .build();\n\n  bahia.add(marta);\n\n```\n\n\n\n### Utilizando Bean Validation\n\nUma 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. \n\nÉ 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.\n\n```java\nimport javax.money.MonetaryAmount;\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.PastOrPresent;\nimport javax.validation.constraints.PositiveOrZero;\nimport java.time.Year;\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic class Player {\n\n    static final Year SOCCER_BORN = Year.of(1863);\n\n    @NotBlank\n    private String name;\n\n    @NotNull\n    @PastOrPresent\n    private Year start;\n\n    @PastOrPresent\n    private Year end;\n\n    @NotNull\n    private Email email;\n\n    @NotNull\n    private Position position;\n\n    @NotNull\n    private MonetaryAmount salary;\n\n    @PositiveOrZero\n    private int goal = 0;\n\n    //continue\n\n}\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Team {\n\n    static final int SIZE = 20;\n\n    @NotBlank\n    private String name;\n\n    @NotNull\n    @Size(max = SIZE)\n    private List<Player> players = new ArrayList<>();\n\n    //continue\n\n}\n\n```\n\nO código fonte desse exemplo está disponível no repositório: [https://github.com/soujava/bulletproof](https://github.com/soujava/bulletproof).\n\n> **TIP**: Lembre-se da importância dos testes de unidade em todo o processo de desenvolvimento!\n\nCom 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.  \n\n## Lombok: problema ou solução?\n\nDe 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:\n\n* 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;\n\n* 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;\n\n* Boa parte dos códigos como getters e setters podem ser gerados pela IDE;\n\n* 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.\n\nQueremos 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.\n\n"
  },
  {
    "path": "manuscript/chapter_04.md",
    "content": "# Clean Architecture {#chapter_04}\n\nÉ 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.\n\n> **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). \n>\n> 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. \n\nIniciaremos falando sobre a estratégia de dividir e conquistar. Essa estratégia possui muitas vantagens, e vamos citar duas dentre elas:\n\n* **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. \n  É 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”. \n\n> **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.\n\n* **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. \n\n> **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.\n\nObserve a imagem a seguir:\n\n![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)\n\n*Fonte: <https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html>*\n\nUsaremos a imagem acima para discorrer sobre pontos de atenção que vale a pena aplicar em sua arquitetura:\n\n\n* Tenha uma estratégia de teste eficaz, que siga a pirâmide de testes;\n* As estruturas devem ser isoladas em módulos individuais. Quando (não se) mudarmos de ideia, precisaremos fazer mudança em apenas um lugar;\n* \"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. \n\n* 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;\n\n* Será um bom monolito com casos de uso claros que você poderá dividir em microsserviços mais tarde, depois de aprender mais sobre eles.\n\n## Granularidade de camadas\n\nAlgo 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. \n\n> **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.\n\nA 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.\n\n### Entidades\n\nCaso 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.\n\n>  **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.\n\n### Casos de uso\n\nNesta 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).\n\n### Interface de adaptação\n\nUm 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. \n\nO 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.\n\n### Frameworks\n\n\nEsta é 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.\n\n\n\n## Conclusão\n\nO 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."
  },
  {
    "path": "manuscript/chapter_05.md",
    "content": "# Refatoração {#chapter_05}\n\nApó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\nNã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.\n\nPara 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).\n\nUma 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:\n\n> \"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*\n\nCom 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.\n\n## Medo de alterar o código que não é seu\n\nSe 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. \n\nEssa 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.\n\nEssa 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.\n\nSituaçõ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.\n\n## A importância dos testes automatizados na hora refatorar\n\nQuanto 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.\n\nSe refatorar código traz riscos, como se pode garantir que erros não serão introduzidos nas suas refatorações?\n\nA 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.\n\nComo 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.\n\nQuanto 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\nNã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.\n\nApesar 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.\n\nO 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?**\n\n## Refatoração contínua do código\n\nUm 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.\n\n## Qual sua motivação para refatorar o código?\n\nExistem diversas motivações para realizar uma refatoração do código, e listamos aqui alguns dos principais:\n\n* **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.\n* **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.\n* **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.\n* **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\").\n\n## Conclusão    \n\nCom 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."
  },
  {
    "path": "manuscript/chapter_06.md",
    "content": "# NoSQL vs. SQL {#chapter_06}\n\nMuitas 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?\n\nPara começar, precisamos falar um pouquinho de história. Com isso, entenderemos os detalhes sobre o NoSQL e suas diferenças para o SQL.\n\nAssim 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:\n\n- 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.\n\nPoré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. \n\nEsse 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.\n\nO 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. \n\n> **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.\n\nEstruturar, 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!\n\nO 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. \n\n> **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).\n\n## NoSQL\n\n### O que significa NoSQL?\n\nNã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.\n\n### Particularidades do NoSQL\n\nQuando 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).\n\nAntes 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.\n\nDevemos destacar duas principais vantagens de um cluster, especialmente em ambientes de banco de dados de alto volume:\n\n* **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;\n\n* **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.\n\n#### Detalhando o conceito BASE\n\n- Base Availability - **BA**\n    - 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;\n\n- Soft State - **S**\n    - 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;\n\n      {pagebreak}\n    \n- Eventually Consistent - **E**\n    - 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.\n\nPara 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.\n\n---\n\n## NoSQL e suas classes\n\nOs bancos de dados NoSQL podem ser categorizados em quatro tipos (que, no contexto de banco de dados, são chamados de **classes**):\n\n* Chave / Valor - `Key / Value`\n* Família de Colunas - `Column Family`\n* Documentos - `Document`\n* Grafos - `Graph`\n\nCada 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.\n\n#### Classe `Key / Value`\n\n![Imagem 06_01: Exemplo de estrutura de dados em um banco Key/Value](images/chapter_06_01.png)\n\nOs 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.\n\nAtualmente existem diversas implementações de banco de dados do tipo chave-valor, dentre os quais os mais famosos são:\n\n* AmazonDynamo\n* AmazonS3\n* Redis\n* Scalaris\n* Voldemort\n\nComparando 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:\n\n| Estrutura relacional | Estrutura chave-valor |\n| -------------------- | --------------------- |\n| Table                | Bucket                |\n| Row                  | Key/value pair        |\n| Column               | ----                  |\n| Relationship         | ----                  |\n\nNessa 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.\n\n#### Classe `Column Family`\n\n![Imagem 06_02: Exemplo de estrutura de dados armazenadas em um banco Column Family.](images/chapter_06_02.png)\n\nEsse 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í. \n\nEm 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.\n\nEsses 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:\n\n* Hbase\n* Cassandra\n* Scylla\n* Clouddata\n* SimpleDb\n* DynamoDB\n\nDentre 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.\n\n\nAo 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:\n\n| Estrutura relacional | Estrutura de família de colunas |\n| -------------------- | ------------------------------- |\n| Table                | Column Family                   |\n| Row                  | Column                          |\n| Column               | Nome e valor da Column          |\n| Relacionamento       | Não tem suporte                 |\n\n#### Classe `Document`\n\nOs 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. \n\n{width=60%}\n\n![Imagem 06_03: Estrutura do dado armazenado em um banco orientado a documentos](images/chapter_06_03.png)\n\nEles 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:\n\n* AmazonSimpleDB\n* ApacheCouchDB\n* MongoDB\n* Riak\n\n\nDestes, 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. \n\n| Estrutura relacional | Estrutura de documentos |\n| -------------------- | ----------------------- |\n| Table                | Collection              |\n| Row                  | Document                |\n| Column               | Key/value pair          |\n| Relationship         | --                      |\n\nUma outra característica de bancos do tipo documento é que, no geral, são _schemeless_.\n\n#### Classe `Graph`\n\n![Imagem 06_04: Como os dados são armazenados em um banco de classe Grafo.](images/chapter_06_04.png \"Estutura de Grafos\")\n\nO 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:\n\n\n* Neo4j\n* InfoGrid\n* Sones\n* HyperGraphDB\n\nDos tipos de banco de dados mais famosos no mundo NoSQL, o grafo possui uma estrutura distinta com o relacional.\n\n#### Multi-model database\n\nAlguns bancos de dados possuem a comum característica de ter suporte de uma ou mais classes apresentadas. São exemplos:\n\n* OrientDB\n* Couchbase\n\n### Teorema do CAP\n\n{width=60%}\n\n![Imagem 06_05: Teorema do CAP.](images/chapter_06_05.png)\n\nUm 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. \n\nEsse teorema afirma que é impossível que o armazenamento de dados distribuído forneça simultaneamente mais de duas das três garantias seguintes:\n\n* *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.\n* *Disponibilidade*: cada pedido recebe uma resposta (sem erro) - sem garantia de que contém a escrita mais recente.\n* *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.\n\nDe 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.\n\nPor 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.\n\nPoré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.\n\n\n\n### Escalabilidade vs Complexidade\n\nNo 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.\n\n![Imagem 06_06: Mapeamento dos tipos de banco de dados e a matriz Escalabilidade vs Complexidade. ](images/chapter_06_06.png)\n\n### Master/Slave vs Masterless\n\nEm linha geral, a persistência no mundo NoSQL possui duas maneiras de comunicação entre os servidores:\n\n\n\n![Imagem 06_07: Estrutura de banco Master/Slave e Masterless.](images/chapter_06_07.png \"Master/Slave vs Masterless\")\n\n* *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.\n* *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.\n\n## Conclusão\n\nEste 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. \n\nOs 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."
  },
  {
    "path": "manuscript/chapter_07.md",
    "content": "# Arquitetura de microsserviços {#chapter_07}\n\nCom 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? \n\nCom 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\". \n\nAntes 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.  \n\n## Arquitetura monolítica\n\nEm 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).\n\n![](images/chapter_07_01.png)\n\nVamos falar dos benefícios dessa arquitetura:\n\n- Facilidade de manutenção, afinal, quanto menos camadas físicas, menos pontos para se verificar, especialmente quando ainda está pequeno;\n- 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.\n- 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.\n- Iniciar um projeto tende a ser muito simples com o monolito - é necessário planejar e arquitetar um ecossistema com menos camadas e componentes.\n- O monitoramento de uma única aplicação é mais simples.\n\nCom 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:\n\n- As chances de se quebrar a aplicação ao realizar alterações pontuais passam a ser cada vez maiores; \n- 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;\n- Um maior tempo de build provoca um processo de entrega mais longo;\n- É 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;\n- 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;\n- A aplicação naturalmente passa a ocupar e consumir maior espaço em memória e possuir um maior tempo de start-up.\n- Mudanças e entregas críticas exigem grande mobilização na organização e planejamento prévio para disponibilização em produção.\n\nNotamos 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. \n\nÉ 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.\n\n## Microsserviços\n\nA 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. \n\n![](images/chapter_07_02.png)\n\nComo apontado por Sam Newman, em seu livro \"Building Microservices\", estes são conceitos que estão implícitos em microsserviços:\n\n* São modelados levando-se em consideração o domínio do negócio;\n* São altamente monitoráveis;\n* Seu deploy pode ser feito de maneira independente dos outros serviços;\n* Possui isolamento às falhas no ecossistema;\n* Detalhes de implementação são \"escondidos\";\n* Automação é essencial em todos os níveis.\n\nCom essas características, alcançamos uma arquitetura flexível e escalável. \n\n> **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.\n\nVejamos algumas vantagens da utilização da abordagem de uma arquitetura orientada a microsserviços:\n\n- **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.\n- **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.\n- **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.\n- **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.\n- **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.\n\n### Desafios da arquitetura de microsserviços\n\nA 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.\n\nAtente-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.\n\nA 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. \n\nA 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). \n\nUm 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.\n\nCuidado 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.\n\n## Migração de um monolito para microservices\n\nForam 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). \n\nCaso 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. \n\n## Os erros mais comuns com microservices \n\nÉ 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:\n\n- 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- 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.\n- 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.\n- [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.\n- 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?\n- 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.\n- [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.\n\n* 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?”.\n\n## Conclusão\n\nComo 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.\n\nMas, 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?\n\nSe 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.\n\nMicrosserviços, como toda decisão de arquitetura, têm suas vantagens e desvantagens. Como diz Martin Fowler:  \n\n> 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)"
  },
  {
    "path": "manuscript/chapter_08.md",
    "content": "# Cloud {#chapter_08}\n\nUma 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*).\n\nDiscorreremos 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. \n\n>  **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. \n\nA 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. \n\n> **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.\n>\n> <https://trends.google.com.br/trends/explore?date=all&q=cloud-native>\n\nO 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.\n\n## Cloud-Native ou Cloud-Enabled?\n\nUma 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*).  \n\n### Cloud-Enabled\n\nÉ 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. \n\nUma 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. \n\nApesar 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. \n\n> **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.\n\nPara entender melhor tudo o que uma aplicação cloud-enabled *não* é capaz de utilizar nativamente, vamos falar sobre o conceito cloud-native.\n\n### Perspectivas sobre o conceito cloud-native\n\nNo momento da escrita deste livro, não há um consenso ou definição exata acerca do termo. Portanto, vejamos posicionamentos:\n\n> \"Cloud-native é uma abordagem para criar e executar aplicações que explora as vantagens do modelo de computação em nuvem. (...)\"\n>\n> —[VMWare Tanzu (Pivotal)](https://tanzu.vmware.com/cloud-native)\n\n> \"Cloud-native é uma maneira diferente de pensar e raciocinar sobre sistemas de software. Ele incorpora os seguintes conceitos: alimentado por infraestrutura descartável, composta por limites, escala globalmente, adota a arquitetura descartável. (...)\"\n>\n> — [Architecting Cloud Native Applications](https://www.amazon.com/Architecting-Cloud-Native-Applications-high-performing-ebook/dp/B07QTJ8WW8/ref=sr_1_4?keywords=cloud+native+applications&qid=1575059989&sr=8-4)\n\n> \"De maneira geral, “cloud-native” é uma abordagem para criar e executar aplicações que explora as vantagens do modelo de entrega de computação em nuvem. \"Cloud-native\" é sobre como as aplicações são criadas e implantadas, não onde. (...)\" \n>\n> — [InfoWorld](https://www.infoworld.com/article/3281046/what-is-cloud-native-the-modern-way-to-develop-software.html)\n\n> \"As tecnologias cloud-native capacitam as empresas a criar e executar aplicações escaláveis em ambientes modernos e dinâmicos, como públicos, privados e nuvens híbridas. Containers, service meshes, microservices, infraestrutura imutável e APIs declarativas exemplificam essa abordagem. (...) \"\n>\n> — [Cloud-Native Computing Foundation](https://www.cncf.io/)\n\n> 'Cloud native' é um adjetivo que descreve as aplicações, arquiteturas, plataformas/infraestrutura e processos que, juntos, tornam o trabalho *econômico* de uma forma que nos permite melhorar nossa capacidade de responder rapidamente às mudanças e reduzir a imprevisibilidade\n>\n> — [Christian Posta](https://www.infoq.com/articles/cloud-native-panel/)\n\n> \"Um conjunto de boas práticas para otimizar uma aplicação na nuvem por meio de: conteinerização, orquestração e automação.\"\n>\n> —  [Otávio Santana](https://twitter.com/otaviojava)\n\nAlém dos conceitos acima, um conjunto de padrões bem recebido pela comunidade é o [12-factor](https://12factor.net/). As excelentes práticas de arquitetura de software sugeridas nessa metodologia originam do livro [Patterns of Enterprise Application Architecture, escrito por Martin Fowler e David Rice](https://books.google.com.br/books/about/Patterns_of_enterprise_application_archi.html?id=FyWZt5DdvFkC&redir_esc=y). Tendo em mente que o livro foi publicado em 2003, tempos em que ainda não se falava em \"cloud-native\", podemos considerar que, ao adotar os conceitos do 12-factor, você estará não apenas criando uma aplicação cloud-native, mas também implementando boas práticas arquiteturais e culturais no desenvolvimento e entrega de software.\n\n> **TIP**: Nomes de projetos que serão detalhados a seguir podem evoluir e mudar com o tempo. Porém as definições e padrões esperados de uma aplicação cloud-native permanecerão. Portanto, recomendamos a leitura das referências citadas e aprofundamento no entendimento do conceito. \n\n### Capabilities for cloud\n\nVeja alguns dos recursos que estão disponíveis e são comumente utilizados em suas aplicações que são concebidas para rodar na cloud:\n\n- Gerência de configurações (Configuration Management)\n- API Management\n- Scheduling (of workloads)\n- Distributed Tracing\n- Service Security\n- Centralized Metrics\n- Auto Scaling / Self healing\n- Service Discovery and Load Balancing\n- Centralized Logging\n\nMais adiante discorreremos sobre um set de tecnologias disponíveis que implementam as features acima no ambiente. Neste momento, o que se deve ter em mente é que, para usufruir dessas funcionalidades que existem no ecossistema, você precisa também ajustar um pouco seu código na aplicação. Caso queira ver na prática um excelente exemplo de como usufruir e implementar funcionalidades cloud-native, recomendo que você baixe e verifique o código da aplicação de exemplo gerada pelo site da especificação [Microprofile](http://microprofile.io/). \n\n> **INFO:** Por trazer um runtime mais leve, a especificação Microprofile abriu um leque de possibilidades para o Java no mundo da cloud. Existem diversas implementações, como Payara Micro, Open Liberty, Quarkus, Helidon e outros. \n\nO Microprofile tem evoluído de forma rápida, e para garantir um conteúdo atualizado, optamos por não incluir todo o código de boas práticas, mas sim instruir você sobre como obter o conteúdo mais recente e de acordo com sua necessidade. \n\nPara criar um projeto em que você pode estudar exemplos de implementação de práticas e funcionalidades cloud-native utilizando-se da especificação MicroProfile, execute os passos a seguir:\n\n1. Acesse o site <https://start.microprofile.io/>\n2. Insira um `groupId`, `artifactId`, selecione uma versão do MicroProfile, versão do Java SE, e o `runtime`. \n   O `runtime ` será a implementação da especificação MicroProfile. \n\n![Imagem 08_01: Página de criação de aplicações MicroProfile - MicroProfile Starter](images/chapter_08_04.png)\n\n3. Clique em `Download`. \n\nSerá realizado o download de dois projetos em sua máquina. Você pode iniciar ambos, testá-los e analisar a simplicidade de se implementarem padrões cloud-native. \n\n>  **INFO:** MicroProfile é uma especificação recomendada para a criação de microsserviços Java. Esteja atento ao fato de que entregar microsserviços **não** é sinônimo de entregar aplicações cloud-native.\n\nVeja no código como são realizadas a implementação das apis de `Health Checks` com liveness e readiness (apis que serão consumidas pelo fato de o orquestrador de containers aumentar a resiliência e suportar processos de deploy ao validar a saúde do pod), de métricas, tracing distribuído, resiliência a timeouts, segurança com JWT, injeção de propriedades de configuração através de anotações, e a utilização de RestClient (permite consumir um serviço rest apenas implementando-se uma interface no serviço cliente). A aplicação de exemplo acima é uma aplicação que inclui *várias*, mas não *todas* as features que iremos discutir.\n\nAlém da utilização da especificação MicroProfile para entrega de microsserviços cloud-native, outra ferramenta amplamente utilizada é o [Spring Boot](https://spring.io/projects/spring-boot). É possível também construir serviços que usufruem de capabilities de aplicações cloud-native com a utilização de Spring e sua stack. [Spring Cloud](https://cloud.spring.io/spring-cloud-static/spring-cloud.html) é um dos frameworks disponíveis que permitem entregar aplicações cloud-native e Java. Assim como demonstrado para o MicroProfile, também é possível criar aplicações de uma forma simples: https://start.spring.io/. Note que, ao clicar em `Add Dependencies`, você pode filtrar por cloud e escolher os componentes que deseja habilitar em sua aplicação:\n\n![Imagem 08_02: Página de criação de aplicações Spring - Spring initializr.](images/chapter_08_06.png)\n\nAgora, uma vez que falamos sobre alguns detalhes de implementação da aplicação propriamente dita, vamos seguir em frente e entender melhores práticas de conteinerização dessas aplicações. \n\n### Princípios de design de conteinerização de aplicações\n\nEm se tratando de detalhes de implementação de uma aplicação cloud-native, é consenso entre as fontes citadas que essas apps serão **conteinerizadas**. Com isso em mente, o(a) arquiteto(a) deve se preocupar não apenas com adoção do  [SOLID](https://www.amazon.com.br/Clean-Architecture-Craftsmans-Software-Structure-ebook/dp/B075LRM681/ref=sr_1_2?adgrpid=83848702769&gclid=CjwKCAjwqpP2BRBTEiwAfpiD-w6m0nZtj0_jaXw7DfCfvIuztN-m6OrPIQ5BH2g2UHzLOird4mProRoCNTYQAvD_BwE&hvadid=426015975773&hvdev=c&hvlocphy=1001541&hvnetw=g&hvqmt=b&hvrand=11863819395550854586&hvtargid=kwd-298463329082&hydadcr=5628_11235154&keywords=clean+architecture&qid=1590032086&sr=8-2) em seu código, mas também com os [princípios de design de conteinerização de aplicações](https://www.redhat.com/en/resources/cloud-native-container-design-whitepaper).\n\nPrincípios a serem considerados durante o tempo de **construção** de uma *imagem*:\n\n* **Single Concern Principle**: Similar ao **S** do padrão **S**OLID, porém, neste contexto, cada container deve resolver *um* problema, e resolvê-lo *bem*; caso seja necessário acoplar mais features a um microservice, por exemplo, adicionar side-cars ao pod é uma boa alternativa.\n* **Self-Containment Principle**: Devem estar contidas na imagem de build todas as bibliotecas, runtime da linguagem e ferramentas de construção necessárias para se realizar o build a aplicação. As exceções são dados que variam entre ambientes, dados estes que estarão, por exemplo, em variáveis de ambiente.\n* **Image Immutability Principle**: Imagens imutáveis são essenciais para permitir escalabilidade e adoção de estratégias de deploy. Diferenças entre ambientes são providas ao container através de configurações (por exemplo, utilizando-se variáveis de ambiente ou `ConfigMaps`);\n\nPrincípios a serem considerados durante o tempo de **runtime** (execução, *container*):\n\n* **High Observability Principle**: A aplicação conteinerizada deve prover as APIs padronizadas para que o orquestrador possa fazer health-checks de liveness e readiness. Coletas de logs e métricas também são parte das APIs que o orquestrador poderá consumir e disponibilizar através de ferramentas como, por exemplo, Fluentd, Logstash para logs centralizados, ou Prometheus e Grafana para métricas. \n* **Lifecycle conformance principle**: É recomendado realizar graceful shutdown dos serviços sempre que possível, certo? Com containers, essa prática também é válida. O container precisa, através de APIs, permitir que o orquestrador envie comandos para graceful ou forceful shutdown. E, caso necessário, pode até mesmo fazer uso de APIs como \"pre-stop\" e \"post-stop\" para implementação de necessidades específicas do componente pertencente ao container.\n* **Process disposability principle**: Deve-se ter em mente que o container é volátil, e pode ser destruído e recriado várias vezes. Desta forma, considere o tempo de start-up e shutdown de seus containers. Além disso, caso seja necessário manter estado, deve-se recorrer a bancos de dados ou volumes providos pela plataforma.\n* **Runtime confinement principle**: Ao seguir o princípio **Self-Containment Principle** em tempo de build, a imagem empregada para se gerar o container utilizado de fato para executar a aplicação terá uma menor necessidade de consumo de recursos (memória, espaço, cpu). O princípio de **Runtime Confinement** espera que o container tenha definidos os limites de recursos que serão utilizados. Com base nisso, o orquestrador poderá fazer uma melhor utilização da infraestrutura disponível.\n\nCom base nesses princípios, é possível notar que não basta apenas criar um `Dockerfile`, conteinerizar uma aplicação e categorizá-la como \"cloud-native\". No processo de conteinerização, é esperado que a aplicação implemente e disponibilize recursos que facilitarão seu gerenciamento, monitoramento e orquestração.\n\n### O ciclo de vida de uma aplicação cloud-native\n\nA componentização dos serviços permite agora que os times de desenvolvimento entreguem mudanças com maior velocidade. Com isso, o time de operações precisa responder de forma equivalente, entregando serviços que permitam entregas velozes, porém estáveis. Para atingir o cenário ideal, é de comum entendimento que a automação de processos de TI é essencial não só para o aumento da produtividade, mas também para o aceleramento da evolução da organização como um todo. \n\nNos cenários atuais, clientes esperam uma evolução constante e serviços altamente disponíveis e performáticos. Vejamos um cenário que considera essa necessidade e demonstra o ciclo de vida da aplicação, utilizando-se de melhores práticas de implantação.\n\nConsiderando que sua aplicação está pronta para deploy:\n\n* O código fonte deve ser disponibilizado em um repositório de código fonte - *a single source of truth*.\n\n  > **TIP**: Conforme [pesquisas](https://softwareengineering.stackexchange.com/questions/136079/are-there-any-statistics-that-show-the-popularity-of-git-versus-svn) sobre os dados de 2020 (baseadas em códigos open-source), repositórios `git` são atualmente a escolha mais popular.\n\n* Esse repositório, por sua vez, pode ser configurado de forma que ações-chave - como criação de uma nova tag, ou commit no branch master - disparem automaticamente uma ferramenta de integração contínua. \n\n* A ferramenta iniciará a execução de testes unitários e o empacotamento dessa aplicação (podendo incluir mais passos, como, por exemplo, checagem da qualidade do código). \n\n  > **TIP:** Tendo em mente uma melhor utilização de recursos, deve-se destacar que: a imagem-base utilizada para construir a aplicação *não* deve ser a mesma imagem-base utilizada para se executar a aplicação. Ferramentas para construção e empacotamento (como maven, npm etc.) que não são utilizadas na execução da aplicação *não* devem estar incluídas na imagem-base de execução.\n\n* Após empacotar a aplicação, a ferramenta de integração é responsável pela criação do container com base na imagem-base que inclua esse novo pacote (i.e. `.jar`).  \n\n  > **TIP:** É recomendado que a imagem-base gerada seja armazenada em um registro de imagens.\n\n* Deve haver a preocupação de criar os arquivos de definição que serão usados para disponibilizar sua aplicação na cloud. Em se falando de Kubernetes por exemplo, deve-se criar os arquivos YAML, manter seu versionamento e processo de release;\n\n* A partir dessa imagem, a plataforma utilizada (i.e. Kubernetes) irá criar a quantidade de containers especificada. \n\n  > **TIP:** É muito comum a existência de casos de uso que podem usufruir da utilização de Operators para gerenciamento e manutenção de aplicações. \n\nAutomação dos processos de entrega de software através da utilização de Entrega Contínua e Deploy Contínuo (CI/CD) é uma das práticas da cultura DevOps. Essa prática se mostrou eficaz em ambientes tradicionais e, agora, se mostra indispensável quando se trabalha com times menores e centenas de microsserviços rodando em clouds privadas, públicas ou híbridas. \n\nO processo acima descrito chega a ser simplista diante da qualidade e eficiência que podemos agregar ao nosso processo de integração. Vamos dar um passo atrás e entender melhor o que seria integração contínua.\n\n#### Integração e Entrega Contínua\n\nComeço este tópico com uma pergunta para você:\n\n>  **Você e sua organização estão prontos para colocar em produção uma nova versão do seu software a cada mudança?**\n\nEste é o estado da arte da integração e entrega contínua. Mas calma, mesmo que sua resposta seja não, a integração e entrega contínua ainda devem fazer parte da sua jornada cloud-native. **Integração** Contínua e **Entrega** Contínua são temas tão vastos que cada um possui seu próprio livro. Mas vamos discorrer sobre os principais tópicos a seguir.\n\nPrimeiro de tudo, a base: **automação de tarefas**. \"Mas até onde devo automatizar?\", você me pergunta. Vamos lá:\n\n#### Integração Contínua (CI)\n\nUtilize uma ferramenta que permita a você automatizar o processo de integração da sua aplicação. A integração é contínua, ou seja, a cada commit no branch master a sua ferramenta de automação deve:\n\n- Compilar e executar o build da aplicação;\n\n- Executar testes unitários e obter o percentual da cobertura de testes;\n- Executar testes para validar a qualidade do código;\n- Enviar notificação ao time, caso qualquer um dos itens acima falhe, de acordo com seus critérios (cobertura de testes aceitável? Qualidade do código aceitável?);\n\nOs passos mencionados devem ser facilmente executáveis, quer dizer, ao clique de um botão. Devem ser reproduzidos de ponta a ponta sem necessidade de intervenção humana.\n\nCom as regras citadas, as pessoas desenvolvedoras no seu time terão sempre a boa prática de versionar código que roda e de testar o build localmente *antes* de se comitar no branch master. Se um commit quebra qualquer uma das regras, deve-se imediatamente corrigir o problema. \n\nEssas práticas tornarão o seu processo de desenvolvimento e release mais confiáveis e estáveis.\n\n#### Entrega Contínua (CD)\n\nUma vez que, com a utilização de CI, você agora gera pacotes mais confiáveis, de acordo com os critérios da sua organização, o que te impede de entregar versões novas com mais frequência em ambiente produtivo? Vamos falar sobre automação do processo de deploy.\n\n> Entrega Contínua != Deploy Contínuo\n\nCom Entrega Contínua, você estaria pronto para liberar - através de um deployment pipeline - a versão recente mais estável do seu código a qualquer momento em produção. Novamente, com um clique de um botão. \n\nE não podemos deixar de falar sobre **Deploy Contínuo**, que é quando você libera - através de um deployment pipeline - uma nova versão em produção a cada commit no branch master. Empresas grandes que praticam deploy contínuo chegam a liberar dezenas de centenas de versões em produção diariamente. \n\n#### Automação é o segredo\n\nPare por um instante e identifique o nível de automação que você possui no momento. Quanto maior a maturidade das práticas de CI mencionadas, mais confiante você se sentirá ao liberar novas releases do seu software. E quanto mais frequentemente você liberar software, mais rápido você terá feedback do(a) usuário(a) final, e o mais importante: maior a garantia de entregar software que funciona e que entrega exatamente o que clientes precisam.  \n\nTenha em mente:\n\n- CI é o primeiro passo;\n- Para realizar Entrega Contínua, você precisa praticar CI. \n- Para praticar Deploy Contínuo, você precisa estar apto e praticando Entrega Contínua.\n\n#### Estratégias de deployment \n\nEscolha dentre estratégias de deploy maduras que sejam mais apropriadas à sua aplicação para permitir entregas mais confiáveis. Ao escolher a estratégia de deploy para sua aplicação, escolha com base no quão importante é: \n\n- Tempo de indisponibilidade - o quão crítico é para o negócio que sua aplicação fique fora do ar durante o deploy?\n- Sua aplicação suportaria que você executasse duas versões (antiga e a nova) ao mesmo tempo?\n- Você gostaria que um determinado grupo de usuários(as) fizesse testes na sua nova release, antes que você libere para 100% dos(as) usuários(as)?\n- Você não possui um grupo de usuários(as) de testes, mas, mesmo assim, gostaria de avaliar o funcionamento da nova versão, liberando a release apenas para um percentual de usuários(as) finais?\n\nVeja algumas estratégias de deploy que você pode usar de maneira fácil com plataformas de orquestração como Kubernetes:\n\n* **Recreate Strategy**: todos os pods existentes serão escalados a zero, e só então o Kubernetes criará pods com a nova versão do seu código. Uma estratégia ousada (tudo ou nada), mas que pode ser necessária em casos de mudanças radicais em estruturas de dados, ou caso você não possa rodar as duas versões simultaneamente em produção;\n\n* **Canary release**: é um tipo de Rolling Strategy, em que se libera a nova versão e, apenas quando constatado que a nova versão é saudável (de acordo com o readiness check do Kubernetes), o Kubernetes começará a destruir os pods com a versão antiga; neste cenário, os pods novos e antigos precisam coexistir durante o período de deploy;\n\n* **Blue-Green**: é uma boa estratégia para se mitigarem falhas, porém é mais custoso. Utilize caso você queira que um grupo de pessoas realize testes para garantir que a nova versão está de fato pronta e pode ir ao ar. Devem existir dois ambientes de produção idênticos, o azul e o verde, mas apenas um estará ativo por vez. Você terá um router que irá direcionar os usuários para o ambiente ativo.\n\n  > **TIP:** Leitura recomendada sobre blue-green deployment: artigo [BlueGreenDeployment,por Martin Fowler](https://martinfowler.com/bliki/BlueGreenDeployment.html).\n\n  Digamos que o ambiente azul está ativo, rodando seu código v1. O grupo de usuários(as) realizará os testes no ambiente verde, não ativo, na versão v2. Uma vez confirmado que a nova versão, v2, pode ir ao ar, você vira a chave, e todas as pessoas usuárias passam a utilizar, agora, o ambiente verde. \n  Seguindo a mesma linha, como o ambiente verde agora está em produção, em um próximo deploy você usaria o ambiente azul para garantir o release antes de virar a chave, e assim por diante.\n\n* **A/B testing**: neste cenário você executa duas versões da aplicação em produção ao mesmo tempo, como uma forma de se testar uma hipótese. Você pode, por exemplo, comparar, durante o período de uma semana, qual das duas versões vai performar melhor. Ou, de uma outra perspectiva, se o fato de adicionar um novo botão na tela da aplicação na nova versão leva os(as) usuários(as) a comprarem mais. Uma vez realizados os testes, pode-se liberar a versão desejada em sua totalidade, usando por exemplo canary release. \n\n## A jornada cloud-native\n\nEntendidos os conceitos que giram em torno de uma aplicação cloud-native e após visualizar o ciclo de vida de uma aplicação cloud-native, o próximo passo é entender as formas que essas aplicações podem ser entregues e as opções disponíveis no mercado. \n\nHá de se concordar que a transformação digital rumo a cloud acarreta não só uma mudança na forma de se codificarem aplicações (time de desenvolvedores), mas se estende a outras áreas da organização de T.I., que precisarão se reformular e lidar com novos desafios. Cada organização se encontra em um momento diferente, possui budgets diferentes e times com características e perfis variados.\n\nVamos discorrer sobre como *infraestrutura como serviço (Infrastructure As A Service - IaaS)*, *plataforma como serviço (Platform As A Service - PaaS)* e *software como serviço (Software As A Service - SaaS )* coexistem, seus prós e contras e como cada uma delas pode auxiliar no momento atual de sua organização na jornada cloud-native.\n\n## IaaS, PaaS e SaaS: uma perspectiva arquitetural\n\nA melhor maneira de se pensar no cloud em termos de abstração para o negócio certamente é na analogia de um serviço de pizzaria como serviço. Podemos partir do cenário em que temos a opção de preparar toda a pizza em casa e ter que gerenciar todo o processo de criação e cozimento, ou sair para comer a pizza em um restaurante sem se preocupar com sua criação. \n\n{width=120%}\n![Imagem 08_03: Representação dos diferentes tipos de oferta de cloud através do exemplo \"Pizza as a Service\".](images/chapter_08_01.png)\n\nNa imagem acima, temos dispostos quatro formatos: on-premise, IaaS, PaaS e SaaS. As caixinhas brancas são as tarefas de nossa responsabilidade, e as azuis são de responsabilidade de terceiros. Essa mesma comparação pode ser feita se trocarmos as tarefas de criação de pizza por tarefas referentes a criação de software: instalação e gerenciamento de sistema operacional, networking, storage, orquestração de serviços, gerenciamento de middleware, runtime, pipelines de CI/CD, e até a criação da aplicação propriamente dita.\n\nQuando falamos de cloud e seus serviços, note que quanto menor a abstração que utilizamos como serviço, por exemplo, com IaaS, maior a responsabilidade no processo de construção do software. Esse grande número de processos crescerá na mesma medida que o número de servidores aumenta, e essa complexidade é diretamente proporcional aos riscos. Em contrapartida, quanto menor a abstração, maior o controle da possibilidade de customização da arquitetura e componentes.\n\n\n\n![Imagem 08_03: Matriz Complexidade vs Risco dos diferentes tipos de oferta de cloud.](images/chapter_08_02.png)\n\n### IaaS - Infra as a Service\n\nCom esta abordagem, é possível obter todos os benefícios de um ambiente de cloud, porém toda a responsabilidade de manutenção do software é da sua equipe de T.I. A empresa que provê o serviço de IaaS tem o dever de garantir a comunicação entre os serviços, de lidar com quedas, problemas de hardware e eventuais consertos. \n\nNeste cenário, a sua equipe assume tarefas referentes a banco de dados, backup, escalonamento tanto vertical quanto horizontal etc. Esse fator aumenta a possibilidade de customização e, por outra perspectiva, gera complexidade e mais risco. Nesta opção o hardware e a garantia de seu funcionamento pertencem a terceiros, mas o serviço é executado pelo seu time; desta forma, pode ter um custo mais reduzido, se comparado a outras opções.\n\nO conceito de *orquestração* no contexto de IaaS é a configuração, gerenciamento e coordenação automatizada dos serviços, aplicações e sistemas de computador. Essa orquestração permite a manutenção da infraestrutura através de programação (infra as code) e tem como objetivo facilitar e abstrair a utilização da IaaS.\n\nExemplos de IaaS são Microsoft Azure, Google Compute Engine e [Amazon EC2](https://aws.amazon.com/ec2/). De uma maneira geral, pode-se pensar nela como sendo um grande aluguel de máquinas por cujo uso se pode pagar, semelhante à nossa conta de luz. Para ilustrar isso, vamos analisar a realização do deploy de uma aplicação conteinerizada no ambiente da Amazon. A [primeira tarefa]((https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/launching-instance.html)) é a criação de uma instância, atividade que requer cerca de sete passos. Em seguida, a [instalação do Docker](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html), para que assim seja possível criar [uma imagem para executar essa instância](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html#docker-basics-create-image). Neste caso, nota-se que um simples deploy de uma aplicação requer não apenas know-how extra como a execução de tarefas de sysadmin por parte de seu time.\n\n### PaaS - Platform as a Service\n\nUma redução drástica de complexidade para focar a criação do software, certamente, é a maior vantagem dentro do PaaS. Com ele, no geral, não é necessário se preocupar com manutenção das máquinas, criação de rotina de backup, compra de licenças etc. Todos os cuidados do [desenvolvimento serão na criação do software](https://twitter.com/gilzow/status/1251308583427465216). Porém o PaaS costuma apresentar custo mais elevado, se comparado com serviços sem maior abstração. \n\n> **TIP:** Veja abaixo algumas opções de PaaS atualmente disponíveis no mercado: \n>\n> * [Platform.sh](https://platform.sh/): é um PaaS que utiliza todos os conceitos de infraestrutura como serviço e também é orientado ao Git, além de possibilitar realizar o deploy da aplicação, deixando todo trabalho para a plataforma. Basicamente, a partir de três arquivos [aplicação](https://docs.platform.sh/configuration/app-containers.html), [serviços](https://docs.platform.sh/configuration/services.html) e [rotas](https://docs.platform.sh/configuration/routes.html), podemos fazer o push para um repositório Git. Um simples push para o sistema remoto do Platform.sh criará automaticamente os containers da aplicação, dos serviços como banco de dados e as rotas da aplicação. Nesse caso, a abstração é gigantesca e faz com que o time foque muito mais a aplicação central da empresa.\n> * [Red Hat OpenShift Online](https://www.openshift.com/products/online/): uma opção para se utilizar o OpenShift (a.k.a. [OKD](https://www.okd.io/)) como serviço. Neste PaaS, o OpenShift é disponibilizado na AWS e permite a desenvolvedores de aplicações Ruby, PHP, Node.js e Java utilizarem seus runtimes e banco de dados de preferência para rodar suas aplicações. Possui uma opção self-service e free para pessoas desenvolvedoras. \n> * [Heroku](https://www.heroku.com/): nascido em 2007 exclusivamente para Ruby, hoje o Heroku suporta as mais diversas linguagens, como Go, Java, PHP, Node.JS e outras, além de várias ferramentas do ecossistema de uma aplicação;\n> * [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine): o PaaS oferecido pelo Google é totalmente baseado em Kubernetes Vanilla. Simples de se iniciar, ao se cadastrar você ganha uma quantidade de créditos para poder rodar seus workloads, e recebe também acesso a outros produtos da Google.\n\nCom base em leituras e conceito aplicação de PaaS por diversas empresas, podemos analisar por duas perspectivas:\n\n1. Você delega o gerenciamento de toda a plataforma (leia-se Kubernetes e similares) para terceiros. Desta forma, a sua equipe não precisa instalar, lidar com manutenções, backups e monitoring. Essa perspectiva é apenas viável quando o caso de uso pode usufruir de uma cloud pública. Heroku e os vários flavors de Kubernetes, como Platform.sh, GKE e OpenShift Online, são exemplos de solução de PaaS que se adequam a essa perspectiva.\n2. No segundo cenário, devido a restrições na sua empresa, você necessariamente deve utilizar uma cloud privada. Nesse caso, você pode prover um PaaS ao disponibilizar uma ferramenta que aumente a autonomia da pessoa desenvolvedora ao prover uma forma eficiente e automatizada de entregar aplicações conteinerizadas, seja através do uso de catálogo de serviços, seja por abstração de apis de entrega de containers ou automação de deploy. Dessa perspectiva, é válido considerar que ferramentas como, por exemplo, OpenShift, instalado em uma cloud-privada e gerenciada pelo seu time de infraestrutura.\n\nEm ambos os cenários, é comum assumir que há maior autonomia da pessoa desenvolvedora ao se utilizar PaaS.\n\n### SaaS - Software as a Service\n\nO software como serviço é a oferta que provê uma solução mais rápida para determinado problema. Nessa oferta, clientes optam por consumir um programa pronto para uso e não precisam se preocupar com hospedagem, escalabilidade etc., e nem mesmo com desenvolvimento. Toda a complexidade e o risco já foram resolvidos. No entanto, a customização é bem reduzida, e a possibilidade de configuração depende diretamente do provedor. \n\n### Conclusão sobre IaaS, Paas e SaaS\n\nDe uma maneira geral, temos que pensar nos seguintes três princípios cíclicos, que comparam:\n\n* Quanto maior a complexidade, menor a abstração;\n* Quanto maior a abstração, menor o risco;\n* Quanto menor o risco, menor a complexidade.\n\nÉ verdade que existem vários benefícios na navegação nos mares da computação em nuvem, porém é muito importante conhecer os tipos de serviços que a cloud disponibiliza e fazer uma análise profunda da complexidade de um serviço e seu respectivo risco, além do tempo que o time está disposto a gastar para criar e manter toda a infraestrutura. O PaaS fornece uma grande vantagem de abstração de toda a infraestrutura e manutenção para que a pessoa desenvolvedora foque seu negócio. O IaaS te garantirá grande flexibilidade e poder para instalar e configurar o que seu time deseja sem nenhum problema, mesmo que tudo tenha que ser configurado manualmente. É muito importante que tanto o time quanto a empresa tenham noção de que, independentemente da escolha, haverá benefícios e desvantagens. É muito importante para que os(as) arquitetos(as) avaliem o que melhor se encaixa na instituição, afinal, cloud e computação não estão relacionados ao quando, mas ao como.\n\n## Kubernetes - quando usar e quando não usar\n\nComo decidir quando é a hora de migrar seu workload para uma arquitetura conteinerizada e orquestrada por plataformas como Kubernetes? Entenda mudanças culturais e operacionais para tomar uma decisão mais embasada. Os pontos abaixo descritos são válidos para plataformas como Kubernetes e seus vários flavors.\n\n* Instalação e manutenção da plataforma de orquestração: em caso de adoção de IaaS, o time de sysadmins deve estar preparado para realizar e gerenciar a instalação de um ou mais clusters de Kubernetes. Deve estar antenado em melhores práticas de gerenciamento e configuração de clusters Kubernetes.\n\n* Utilização da plataforma Kubernetes: em caso de IaaS ou PaaS:\n\n  * Da perspectiva de pessoas desenvolvedoras, a conteinerização da aplicação garante que o código que roda em sua máquina funcionará também nos ambientes produtivos. Basta garantir que a imagem seja corretamente descrita. Além disso, uma vez absorvidos os conceitos de criação de uma aplicação cloud-native e as vantagens de um ambiente de orquestração, o(a) desenvolvedor(a) tem a vantagem da perspectiva da carreira, de estar apto(a) a trabalhar em diferentes vendors, uma vez que há certa padronização na forma de trabalho. Por outro lado, a gestão deve estar ciente da curva de aprendizado inicial que a equipe enfrentará.\n\n    > TIP: É recomendada a leitura do livro [Kubernetes patterns for designing cloud-native apps](https://www.redhat.com/en/resources/oreilly-kubernetes-patterns-cloud-native-apps). Traz boas práticas na implementação de serviços cloud-native. \n\n  * Da perspectiva do time de middleware ou sysadmins, o dia a dia passa a ser diferente. Não é mais necessário despender tempo aprendendo como entregar diferentes tipos de aplicação e desvendando suas respectivas peculiaridades e dependências. Basta aprender a lidar com containers, e a forma de trabalho passa a ser padrão, independentemente da tecnologia utilizada nas aplicações. Por outro lado, deve-se entender conceitos de operators, segregação e segurança no ambiente Kubernetes, estratégias de deployment (rolling, blue-green, canary), como lidar com storages etc.\n\n\n##  Kubernetes Vanilla e seus sabores\n\nEsse projeto se popularizou por demonstrar que é capaz de lidar com orquestração de containers em larga escala. É possível identificar diversas ofertas baseadas em Kubernetes: Heptio, OpenShift, Platform.sh, Rancher, entre outras. A intenção é que cada empresa por trás dessas plataforma possa agregar valor com features únicas, porém respeitando todos os conceitos e arquiteturas existentes em sua plataforma de origem, Kubernetes. \n\nAs diferentes plataformas trazem consigo, de maneira pré-selecionada, porém não definitiva, um conjunto de tecnologias que irão atender aos requisitos de uma plataforma de orquestração. Para entender melhor essa afirmação, analise a [CNCF landscape](https://landscape.cncf.io/) e note a quantidade de tecnologias que endereçam, por exemplo, a categoria de Service Mesh. Uma das opções disponíveis é o [Istio](istio.io), solução disponibilizada de forma suportada em plataformas como o OpenShift, o que não significa que a mesma tecnologia é exclusiva dessa plataforma. O que se torna diferenciado entre plataformas são as distribuições utilizadas. Ainda tendo o Service Mesh como exemplo, o projeto de Istio entregue pelo OpenShift é o [Maistra](http://maistra.io/), que é um projeto que adiciona mais features on top of Istio. \n\nNesse contexto, ao selecionar sua próxima plataforma de orquestração, considere:\n\n- As features exclusivas de cada plataforma e como elas podem tornar sua equipe mais eficiente;\n- O quanto é importante para sua empresa o nível de estabilidade do serviço (em casos de ofertas de PaaS em clouds públicas ou híbridas);\n- Como você e sua equipe irão lidar com problemas: por si sós, ou precisam de suporte enterprise?\n- Se vai rodar em cloud pública, valide se a plataforma oferece suporte ou tem cases de sucesso na devida opção (Amazon, Google, Azure etc.).\n- Avalie as ferramentas de CI/CD suportadas pela plataforma;\n- Avalie as ferramentas que seu(sua) desenvolvedor(a) terá que utilizar no dia a dia para interagir com a ferramenta de orquestração: existe apenas CLI? Há um console que permite manutenção e monitoramento do ambiente? \n- Caso seja parte do seu cenário uma migração parcial e de médio/longo prazo de um ambiente on-premise para uma cloud pública, avalie as possibilidades de um ambiente híbrido onde você possa usufruir de ambas as clouds, privada e pública, sem maiores problemas com a plataforma em questão.\n\n### Como escolher seu set de tecnologias\n\n\nExistem diversas opções de implementação de cada uma destas funcionalidades, e você pode ver as diversas opções recomendadas para um cenário de cloud na [CNCF Cloud Native Interactive Landscape](https://landscape.cncf.io/zoom=200). \n\n> **INFO:** Pensando em organizações que estão na jornada para a cloud nasceu a Cloud Native Computing Foundation (CNCF). A CNCF nasceu com o objetivo de definir o termo Cloud Native e fornecer espaço para projetos open source, como Kubernetes, Prometheus e [muito mais](https://landscape.cncf.io/zoom=200). A CNCF adota projetos e os encaixa na arquitetura de cloud computing, segundo a visão dos membros do comitê.\n\nA CNCF assume projetos e os classifica como **graduados**, **incubando** ou em **sandbox**. Essa classificação deriva da maturidade de cada software, de acordo com sua utilização no mercado:\n\n![Imagem 08_04: O que é o \"Chasm\" e os níveis de adoção de tecnologia.](images/chapter_08_05.png)\n\n*Imagem obtida em <https://www.cncf.io/projects/>*\n\nSe tiver a possibilidade, acesse a [CNCF Cloud Native Interactive Landscape](https://landscape.cncf.io/zoom=200) e repare as inúmeras tecnologias disponíveis para solucionar os problemas de um ambiente de nuvem. Note que, apesar de ser hoje a mais popular, Kubernetes é apenas *uma* dentre as opções de orquestração de contêineres. Não podemos deixar de ressaltar algumas tecnologias populares:\n\n* Diversas distribuições de Kubernetes, como [Red Hat OpenShift](https://www.openshift.com/) e [Rancher](https://rancher.com/);\n* Ofertas de PaaS, como [Heroku](https://www.heroku.com/) e [platform.sh](https://platform.sh/);\n* [Jaeger](https://www.jaegertracing.io/) para tracing de aplicações, e [Prometheus](https://prometheus.io/) e [Grafana](https://grafana.com/) para monitoramento;\n* [Strimzi](https://strimzi.io/) e [Apache Spark](https://spark.apache.org/) para streaming e mensageria;\n* [Istio](https://istio.io/) para service mesh, [Envoy](https://www.envoyproxy.io/) para Service proxy;\n* [Jenkins](https://jenkins.io/), [JenkinsX](https://jenkins.io/) e [Tekton](https://tekton.dev/) para CI/CD;\n* [Helm](https://helm.sh/), [Operator Framework](https://coreos.com/operators/) e [Podman](https://podman.io/) para construção de imagens e definição de aplicações.\n\nAinda no contexto de ferramentas e funcionalidades de que aplicações cloud-native podem usufruir, vamos falar a seguir sobre service mesh e as tecnologias existentes.\n\n##### Bonus Topic: Service Mesh\n\nVamos à definição de service mesh (malha de serviços) por [William Morgan, 2017](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one):\n\n> Uma malha de serviço é uma camada de infraestrutura dedicada a lidar com a comunicação serviço a serviço. É responsável pela entrega confiável de solicitações por meio da topologia complexa de serviços que inclui um aplicativo nativo da nuvem moderno. Na prática, a malha de serviço é normalmente implementada como uma matriz de proxies de rede leves que são implantados juntamente com o código do aplicativo, sem que o aplicativo precise estar ciente.\n\nEstamos falando de um desacoplamento entre o \"Dev\" e o \"Ops\", ou melhor, a pessoa desenvolvedora não precisa se preocupar se o código do seu microserviço pode prover as capacidades de que a operação precisa. E toda vez que a operação precisar alterar algo, não é necessária uma nova compilação da aplicação, gerando um desacoplamento que prove a resiliência, a segurança, toda a parte de observability e reteamento para a malha e fora da aplicação.\n\nSe avaliarmos que o conceito começou a ganhar popularidade em 2017, é um conceito um tanto quanto novo, mas a ideia é criar uma camada de abstração por cima da aplicação. E, através da malha, prover a segurança entre os serviços, e também delegar para a malha toda a observação e resiliência de que ela possa precisar.\n\nUma camada de abstração pode existir em três níveis:\n\n- **Biblioteca** - Cada serviço implementa uma biblioteca que inclui as capacidades da malha; blibliotecas como [Hystrix](https://github.com/Netflix/Hystrix) ou [Ribbon](https://github.com/Netflix/ribbon) são exemplos. Essas implementações possuem trade-offs, pois, neste caso, você precisa fornecer as bibliotecas e, com isso, limitar as tecnologias que se pode trabalhar. Se seu projeto tem múltiplas linguagens, você também precisará de múltiplas implementações, e precisará gerenciar esse cenário. \n- **Node Agent** - Existe um agente rodando em cada nó da malha e com a responsabilidade de cuidar das capacidades da malha. A implementação do Linkerd no Kubernetes usa esse modelo. O trade-off aqui é que o time de Ops precisa trabalhar em conjunto com o time de Dev para que as capacidades estejam bem estabelecidas.\n- **Sidecar** - Atualmente, a implementação mais sugerida é o modelo usado pelo Istio com Envoy. Nesse modelo, para cada container de aplicação existirá um container ao lado - no mesmo pod. Esse sidecar lida com tudo que a malha precisa para chegar ou sair do serviço.\n\nMas o que são essas capacidades da `Malha` que são mencionadas? A malha de serviços deve seguir as regras de ORASTAR sem ter códigos implementados no seu microsserviço:\n\n- **Observability** - O Painel de Controle provê todos os serviços de observação que estão rodando no plano de dados. Ou seja, métricas para ver a latência, consumo de banda, logging e tracing, tudo que precisa para monitorar a saúde dos seus serviços ficam na malha, provendo visualizações gráficas de todas as requisições.\n- **Routing** - As regras de roteamento para o controle do tráfego pode ser feito tanto visualmente quanto por arquivos de configuração e, então, enviadas do controle para todas as aplicações. Estamos falando de mudança e divisão de tráfego, controle sobre o que entra e sai e capacidade de injetar falhas e latências para fazer testes.\n- **Automatic Scaling** - O painel de controle tem que ser capaz de escalar automaticamente para lidar com os aumentos ou diminuições do workload.\n- **Separation of duties** - Usar o painel de controle para separar a operação da malha e dar mais independência para as pessoas desenvolvedoras.\n- **Trust** - Delegue para a malha e o plano de dados os protocolos de comunicação de segurança, bem como a renovação e manutenção de certificados. E a regra é sempre a mesma: considere que se está sempre inseguro, mesmo abaixo de um firewall.\n- **Automatic service registration and discovery** - O painel de controle interage com o gerenciamento do cluster para provisionar a descoberta dos serviços automaticamente, assim como registrar a aplicação durante o procedimento de deploy.\n- **Resilient** - As regras de resiliência para toda a sua malha: é necessário assumir que a rede pode falhar. Então é preciso ter a capacidade de blindar o tráfego e, automaticamente, balancear para outros pontos que estejam funcionando e que possam prover a mesma capacidade. Aqui entra o conceito de Circuit Breaker.\n\n###### Arquitetura\n\nE, para implementar as regras de ORASTAR, temos a arquitetura de um painel de controle e um plano de dados. Vamos entender esses conceitos.\n\n`Painel de Controle` é onde ficam as configurações, políticas e a administração dos serviços que estão no plano de dados para o controle de rotas, tráfego, monitoração, descoberta e registro dos serviços. É aqui que está a responsabilidade pela comunicação entre os microsserviços, através de autenticação, autorização e segurança no tráfego da rede.\n\n`Plano de dados` é onde estão todos os microsserviços e seus \"sidecars\", não importando como eles estão implementados. É com essa camada que o painel de controle interage para conseguir controlar os serviços.\n\n\n\n![Imagem 08_05: Represetação de um Data Plane](images/chapter_08_03.png)\n\n###### Ferramentas\n\nService Mesh é um conceito que surgiu por volta de 2018 e está em constante evolução. Em termos de implementação, basicamente vamos falar de três ferramentas: Istio, Linkerd e Consul.\n\n**Istio** - Ferramenta criada pela Google, IBM e Lyft. Hoje o [Istio](https://istio.io/) é a implementação mais recomendada do mercado, e construída em cima da plataforma do Kubernetes. \n\nEle é um painel de controle centralizado que permite que se administre e coordene a aplicação no plano de dados - inclusive pode-se rodar o core dele fora do kubernetes -, tem suporte à integração com VMs e service discovery com diferentes produtos.\n\n**Linkerd** - iniciado pelo Twitter para lidar com seu volume massivo. Hoje está na versão [Linkerd](https://linkerd.io/) 2.0. Possui a limitação de rodar apenas ambientes com Kubernetes. Também possui um painel de controle centralizado como o Istio, e usa seu próprio Sidecar, em vez do Envoy como proxy, como os concorrentes.\n\n**Consul** - criado pela Hashicorp. O [Consul](https://www.consul.io/) é mais velho que o próprio service mesh, sendo que sua primeira versão foi de 2014. Ele é composto de um binário em GO para o servidor e aplicações para prover as capacidades da malha. Mas, diferentemente das ferramentas acima, ele tem um painel de controle distribuído, pois é feito para funcionar próximo às máquinas. \n\n> **TIP:** Para optar por um dos serviços, veja a comparação de funcionalidades entre os serviços encontrada no artigo [Kubernetes Service Mesh: A Comparison of Istio, Linkerd and Consul](https://platform9.com/blog/kubernetes-service-mesh-a-comparison-of-istio-linkerd-and-consul).  \n\nDo ponto de vista do autor, uma das desvantagens do Linkerd é não possuir Circuit Breaker nativo, fazendo com que você tenha que implementar o Circuit Breaker no código de seu serviço. Em contrapartida, ele é o mais fácil de operar, dentre as outras opções.\n\nNão podemos deixar de fora os provedores de serviços na nuvem pública que também têm oferecido service mesh, como a AWS e a Microsoft:\n\n**AWS App Mesh** - Se o seu ambiente está na AWS, vale a pena dar uma olhada neste serviço que usa o Envoy como sidecar proxy: [AWS App Mesh](https://aws.amazon.com/app-mesh/?nc1=h_ls);\n\n**Azure Service Fabric Mesh** - Apesar de usar o nome Service Mesh, este é o que mais difere dos demais. Está mais comparado a um Red Hat OpenShift e se faz necessário que você tenha um plano de dados de malha de serviço já em uso. É um serviço gerenciado, e as pessoas desenvolvedoras não têm acesso ao painel de controle: [Azure Service Fabric](https://azure.microsoft.com/services/service-fabric/).\n\n## Quando não usar Kubernetes\n\nCom base na explicação de aplicações cloud-native, note que, para entregar arquiteturas cloud-native, você não precisa necessariamente utilizar Kubernetes. Principalmente se você está no início da jornada para a cloud, Kubernetes (e seus vários flavors) podem não ser a solução de todas as questões na jornada para a cloud, e pior, talvez até te traga mais pontos de atenção. \n\nSe seu cenário ainda possui um baixo número de imagens e containers, talvez ainda não seja a hora de adotar uma arquitetura mais robusta como Kubernetes. Além do aumento do consumo de recursos, seus times de Dev e Ops precisarão estar aptos a imergir nesse novo paradigma de entrega de aplicações. Talvez seja apropriado iniciar com uma ferramenta como [Docker Swarm](https://docs.docker.com/get-started/swarm-deploy/). O Docker Swarm está disponível junto ao Docker engine que você instala, e sua utilização se dá através do próprio comando `Docker`:\n\n```\n$ docker swarm\n\nUsage:\tdocker swarm COMMAND\n\nManage Swarm\n\nCommands:\n  ca          Display and rotate the root CA\n  init        Initialize a swarm\n  join        Join a swarm as a node and/or manager\n  join-token  Manage join tokens\n  leave       Leave the swarm\n  unlock      Unlock swarm\n  unlock-key  Manage the unlock key\n  update      Update the swarm\n```\n\nVocê pode criar as máquinas que farão parte do cluster, instalar o Docker e configurar o Swarm e a camada rede. Uma vez configurado seu Swarm, você pode escalar as réplicas de pod e gerenciar em uma escala menor, se comparado ao Kubernetes, os seus containers. \n\nCom a introdução do conceito de containers e orquestração de containers ao seu time, naturalmente haverá uma evolução para a automação dos processos de construção e entrega dessas imagens e containers. Com isso, o time terá a chance de praticar e absorver o conhecimento necessário para imergir mais naturalmente em um cenário mais complexo de orquestradores robustos.\n\nDeve-se também ter em mente o cenário em que você já utilizou o Kubernetes e já tem um conhecimento mais aprofundado da ferramenta. Com isso, você já tem ideias de como melhorar ou facilitar os fluxos internos de orquestração, práticas de desenvolvimento, de entrega contínua. Tem ideias de UI que podem auxiliar o(a) usuário(a) ou práticas que podem acelerar a entrega de aplicações. Com base nisso, você pode, ao invés de usar o Kubernetes, partir para um dos flavors disponíveis, e até mesmo criar o seu próprio flavor! \n\n## Conclusão \n\nNão existem dúvidas de que o futuro da tecnologia reside na cloud. Com o surgimento dos microsserviços, cada vez mais precisamos do conhecimento do ecossistema que gira ao seu redor. Com o aumento do uso de containers e práticas de DevOps, cada vez mais as pessoas desenvolvedoras precisam conhecer sobre plataforma, e o system admin precisa conhecer sobre desenvolvimento. \n\nSe ainda não fazem parte da sua realidade, aplicações cloud-ready e cloud-native com certeza o farão em um futuro muito próximo. Junto a elas, todo o vasto ecossistema de containers, orquestração de containers e demais funcionalidades que giram ao redor desses serviços serão necessários. Esteja pronto para aderir ao movimento. \n\n\n\n\n\n"
  },
  {
    "path": "manuscript/chapter_09.md",
    "content": "# Precisamos falar sobre atualizações {#chapter_09}\n\nNão vou mentir pra você: quando ouvi falar que o Java seria atualizado a cada seis meses, pensei comigo: \"xiiiii… esse negócio não vai dar certo\".\n\nOras, e por quê? Por alguns motivos (seis, para ser mais exato):\n\n1. Se olharmos a plataforma Java apenas do ponto de vista de código fonte, já veremos que se trata de um projeto absolutamente gigantesco, que vem sendo evoluído há 25 anos e que, durante esse tempo, passou por mudanças da própria computação em si (vide os casos de cloud e containers, só para citar dois exemplos rápidos);\n2. Milhões de linhas de código, milhares e milhares de classes, um \"buzilhão\" de coisas que dependem de outras tantas;\n3. Cada feature deriva de uma JSR, que não raro está debaixo de um projeto \"guarda-chuva\". Em cada uma dessas partes, muitas vezes, há pessoas diferentes trabalhando, e ritmos diferentes de evolução dos projetos;\n4. Tudo o que acontece dentro da plataforma é regulado pelo JCP, que não é famoso pela sua velocidade;\n5. O ecossistema de ferramentas e frameworks em torno do Java é um dos maiores do mundo (se não for o maior). Alguns open source, outros proprietários. E todos totalmente dependentes do ritmo de evolução da plataforma;\n6. E, finalmente, os(as) usuários(as), que normalmente são desenvolvedores(as) que trabalham em empresas. Sem dúvida, são as pessoas mais impactadas por qualquer questão referente a ciclo de atualizações, já que são a ponta dessa cadeia de interesse.\n\nEu poderia acrescentar mais itens a essa lista, mas creio que ela já é suficiente para justificar o meu ceticismo inicial.\n\nMas hoje cá estamos nós e… não é que eles conseguiram? Sim, hoje temos novidades no Java a cada seis meses! E não apenas funcionou, como está indo bem. Bem demais!\n\nNas próximas linhas vamos conversar sobre implicações e desafios específicos que esse novo ciclo ajuda a resolver e que também serviram de inspiração para que ele fosse adotado.\n\n## Com que frequência você entrega alguma coisa para seu (sua) usuário (a)?\n\nSe você já leu/ouviu/estudou alguma coisa sobre DevOps, talvez já tenha passado por algo parecido com essa pergunta.\n\nSó para entendermos rapidamente o princípio aqui envolvido: suponha que você entrega uma versão/atualização de algum software para o(a) usuário(a) final uma vez ao ano.\n\nOu seja, você e sua equipe trabalharam um ano inteiro para criar uma versão nova. Codificaram muito, mexeram no banco, alteraram configurações, atualizaram telas e tantas outras coisas que podem ser feitas de acordo com cada tipo de aplicação.\n\nAgora vamos olhar para as implicações desse cenário:\n\n1. Você descarregou um ano de trabalho no colo de seu(sua) usuário(a). Claro que você pode tornar essa experiência o menos dolorosa possível. Mas ainda assim vai doer;\n2. Você publicou uma massa imensa de alterações. Se ocorrer um bug, a sua superfície de busca/debug é gigantesca;\n3. Você tinha alterações/melhorias simples, pequenas, que já estavam prontas desde o primeiro mês, mas que tiveram que esperar \"a grande release\" para chegarem até seu(sua) usuário(a).\n\nE, vamos lembrar, estamos falando de um ano. Imagine se você fizer isso a cada dois anos? Ou a cada três anos? \n\nBom, era mais ou menos isso que estava acontecendo com o Java. Normalmente levava-se de dois a três anos para uma nova versão. Aliás, chegamos a incríveis (quase) cinco anos entre a versão 1.6 e a 1.7.\n\nAgora vamos pensar na situação oposta, em que você entrega com uma frequência cada vez maior. O que acontece?\n\n1. Você entrega menos coisas para seu(sua) usuário(a) de uma vez só. É mais fácil ele(a) absorver as mudanças. É mais fácil fazer seu treinamento. Ele(a) vai ter uma maior percepção daquela pequena melhoria fantástica que se perderia no meio de uma release maior;\n2. Se um bug ocorrer, você tem muito menos lugares para procurar. E, como tem menos código entregue, a chance de ter erros também é menor. Tem gente que até prefere nunca entregar nada, assim nunca dá erro…;\n3. As melhorias chegarão às mãos de seus(suas) usuários(as) em um tempo muito menor. Você vai deixá-los(as) mais feliz com uma frequência maior. Ponto pra você!\n\nFoi exatamente o que ocorreu com a plataforma Java. Eles passaram de um período aproximado de três anos entre as releases para outro de seis meses. É uma frequência seis vezes maior.\n\nSó para te dar uma ideia, o JDK 9 teve noventa e um itens em sua release, enquanto o JDK 14 teve apenas dezesseis. Olhe para os três itens que mencionei acima e veja-os materializados na linguagem mais utilizada no mundo.\n\n## Os problemas de não atualizar a versão de JVM\n\nA empresa JRebel publicou no ano de 2020 os resultados de uma pesquisa realizada com centenas de profissionais ao redor do mundo. Veja abaixo um dos resultados específicos em relação às versões de Java:\n\n![Imagem 09_01: Pesquisa de versões do Java mais utilizadas em 2020 feita pela JRebel.](images/chapter_09_01.png)\n*Fonte: <https://www.jrebel.com/blog/2020-java-technology-report>*\n\nOutra pesquisa interessante também, publicada em 2020 pela Snyk, trouxe os seguintes resultados:\n\n![Imagem 09_02: Pesquisa do uso de diferentes versões do Java feita pela Snyk.](images/chapter_09_02.png)\n*Fonte: <https://snyk.io/blog/developers-dont-want-to-leave-java-8-as-64-hold-firm-on-their-preferred-release/>*\n\nTirando apenas a média dessas duas pesquisas, já temos cerca de 60% dos desenvolvedores Java ainda na versão 8. E há pesquisas por aí que dizem que esse número pode ser ainda maior, chegando a algo em torno de 80%.\n\nConsiderando que o Java 8 foi lançado em 2014 e que já temos uma outra LTS (Java 11) desde 2018, deveríamos fazer no mínimo duas perguntas, para as quais já elenco as devidas respostas.\n\n### Por que o mercado ainda está no JDK 8?\n\nVamos encarar a realidade: o mercado, principalmente quando se fala em aplicações enterprise, não anda na mesma velocidade que todos nós gostaríamos.\n\nE por que isso? Eu diria que os principais motivos estão claramente listados na mesma pesquisa da Snyk já mencionada acima:\n\n![Imagem 09_03:  Pesquisa com motivos pelo uso do Java 8, feita pela Snyk.](images/chapter_09_03.png)\n*Fonte: <https://snyk.io/blog/developers-dont-want-to-leave-java-8-as-64-hold-firm-on-their-preferred-release/>*\n\nEsse resultado, aliás, não deveria surpreender ninguém. Os principais motivos que fazem profissionais e empresas ao redor do mundo não terem essa ânsia pelas últimas atualizações são:\n\n1. A versão atual funciona (o famoso \"em time que está ganhando não se mexe\");\n2. O custo de migração é muito alto;\n3. A área de negócios não concorda com a migração.\n\nEu diria que o item 3 é devido ao item 1. Ou seja, a área de negócios não concorda com a migração exatamente porque a versão atual funciona.\n\nPrecisamos ter em mente uma realidade de forma clara: projetos em ambiente corporativo são resultado dos acordos feitos a partir de diferentes interesses. \n\nAtualização de versão (por qualquer motivo que seja) significa risco de erros e indisponibilidade. A área de negócios não quer isso, assim como a área de operações.\n\nNo caso do item 2 (custo de migração elevado), ele também é perfeitamente justificável. Afinal, sempre que pensar em mudar a versão de uma JVM, você terá que:\n\n1. Fazer o build do código atual na versão nova (muitos já desistem aqui);\n2. Fazer todos os ajustes de código necessários para funcionar na versão nova;\n3. Fazer todos os testes necessários para garantir que nenhum erro foi adicionado na aplicação pela pura troca de versão de JVM (e, nessas horas, testes unitários serão seu melhor amigo);\n4. Atualizar as ferramentas do seu ecossistema que dependiam da versão anterior;\n5. Garantir que a nova versão do Java estará instalada e configurada em todos os ambientes em que sua aplicação rodar (container ou VMs);\n6. Se chegar até aqui, provavelmente já terá boa segurança pra migrar.\n\nE, no caso da migração do Java 8 para versões mais atuais, temos ainda um \"pequeno\" detalhe pelo caminho: o Java 9.\n\nSim, Java tem retrocompatibilidade. Sim, um código feito em Java 1.2 deveria funcionar no Java 14. Mas também é verdade que o Java 9 introduziu mudanças brutais na plataforma que, sim, quebraram muito projeto por aí.\n\nEm linhas bem gerais, a modularização lançada no JDK 9 (projeto Jigsaw) trouxe não apenas a possibilidade de você modularizar a sua aplicação internamente, mas a própria plataforma em si foi modularizada. Então a forma como o seu projeto lida com a plataforma Java mudou, e o resultado disso para muitos projetos acabou inviabilizando a saída destes da versão 8.\n\nAliás, existe um artigo fantástico da Trisha Gee falando sobre como migrar seu projeto para o JDK 9 (caso ainda esteja em versões anteriores). Além de útil para a migração em si (claro!), ele ainda vai te dar uma ideia de alguns problemas que podem ocorrer durante o processo. Está aqui: <https://www.infoq.com/br/articles/Java-Jigsaw-Migration-Guide/>\n\nPor todos esses motivos, é de certa forma compreensível que o mercado ainda esteja em sua maior parte \"preso\" ao Java 8. \n\nAliás, de certa forma é até irônico ver que muita gente reclamava que o Java demorava para receber novas atualizações, e agora boa parte dessas pessoas não consegue acompanhar o ritmo de inovações da plataforma.\n\nPorém, se por um lado é compreensível que se pense duzentas vezes antes de migrar para uma nova JVM, é também verdade que ficar parado no tempo tem suas implicações técnicas.\n\n### Quais os impactos de não atualizar para as últimas versões?\n\nHá motivos para que se lancem novas versões de qualquer software: corrigir erros, melhorar funcionalidades, criar outras novas, ou mesmo introduzir tendências que surgem no mercado de tecnologia.\n\nCom a plataforma Java não é diferente. Ela não é atualizada, evoluída e modernizada \"por esporte\". Logo, se há motivos técnicos que justificam sua atualização, os(as) bons(as) arquitetos(as), desenvolvedores(as) e engenheiros(as) do mercado devem prestar atenção a isso.\n\nVamos imaginar que você é um(a) profissional que trabalha em uma aplicação Java que está usando o JDK 8. Vamos supor que, nesse nosso ambiente imaginário, não há interesse em atualizar o Java para as versões mais recentes (mesmo que seja a versão 11, que é a versão LTS - Long Term Support - mais recente em 2020). Vamos olhar alguns poucos e importantes itens que você está perdendo:\n\n* Com a modularização do JDK 9, surgiu também o jlink, que permite que você, de certa forma, gere o seu próprio JRE. Ou seja, você consegue gerar a sua aplicação utilizando estritamente as dependências necessárias. Em um mundo cada vez mais ligado aos containers, é possível gerar imagens até 70% menores utilizando jlink;\n* Falando em containers, até o JDK 8, a plataforma Java não tinha sido concebida para lidar com restrições de memória do processo onde a JVM está rodando (que, no caso dos containers, derivou do cgroups - algo que já existe em sistemas UNIX há décadas). Ou seja, um container rodando até a versão do JDK 8 poderia alocar memória indefinidamente, até que ocupasse todos os recursos disponibilizados para o daemon, o que exigiu que muitos fizessem workarounds (= gambiarras) para evitar maiores problemas. Desde o JDK 9 (vide este artigo <https://www.infoq.com/br/news/2017/03/java-memory-limit-container/>), a cada nova release temos alguma melhoria com relação à gestão de consumo de recursos da plataforma. Hoje podemos dizer que Java é extremamente performático, eficiente e viável para uso com containers;\n* A partir do JDK 10, o compilador JIT do Graal VM está disponível em qualquer distribuição de Java. Ele é em média 13% mais rápido que o JIT padrão, e tem evoluído rapidamente;\n* No JDK 9, o G1 passou a ser o Garbage Collector default da plataforma, o que traz ganhos substanciais de performance se você simplesmente mudar a JVM (sem tocar no seu código);\n* Falando em Garbage Collector, você poderia (quem sabe?) estar avaliando o uso do Shenandoah, um Garbage Collector chamado de \"low-pause-time\", ou seja, que faz a sua coleta praticamente sem gerar overhead na sua JVM.\n\n{pagebreak}\n\nPoderíamos escrever outro livro só listando coisas que as aplicações rodando em uma JVM na versão 8 estão \"perdendo\". E veja que nem foram mencionadas aqui questões de segurança, que é algo crítico em qualquer ambiente corporativo.\n\nÉ claro, tudo é uma questão de balancear interesses e necessidades. Mas, sem dúvida, planejar se manter atualizado ao menos com as versões LTS é algo que deveria estar nos seus planos e nos planos da sua organização.\n\n## Utilidade versus Hype\n\nHá um tempo atrás eu estive em uma grande empresa brasileira para falarmos sobre seu desejo de migrar para uma arquitetura de microservices. Após conversarmos algumas amenidades, fiz a pergunta de um milhão de deploys: por que vocês querem usar microservices?\n\nE a resposta, claro, não poderia ser diferente: _porque o nosso chefe viu num evento e mandou a gente fazer_.\n\nIsso acontece milhares e milhares de vezes ao redor do mundo, diariamente. Empresas e profissionais adotam tecnologias, padrões, abordagens e linguagens sem ao menos saber do que se trata, para que serve, que problemas aquilo resolve e se ao menos elas têm esses problemas. Apenas porque alguém disse que é legal, é novo, é batuta.\n\nNão seja esse(a) profissional! Cada vez que você faz isso, um pod morre em algum cluster de Kubernetes por aí. Ajude a salvar os pods…\n\nO que um(a) bom(a) arquiteto(a) deveria fazer, então? \n\nTer equilíbrio. Como em tudo na vida.\n\nCada dia tem uma nova bala de prata por aí, algo que vai resolver todos os problemas, que você pode utilizar em todos os projetos. É a fórmula do fracasso.\n\nE daí vem o outro extremo: há quem não preste atenção em novidade alguma, não experimenta nada, não faz uma POC sequer para ver a aplicação de alguma nova tendência. Tudo em nome de evitar a hype. Mais gente matando pods por aí.\n\nDe novo, equilíbrio.\n\nA hype em si não é ruim. Tudo o que está consolidado hoje foi hype um dia. O primeiro JavaOne teve mais de seis mil participantes e foi realizado apenas um ano após o lançamento da primeira versão. Era hype! \n\nO segredo é usar a hype a seu favor. Tirar proveito dela.\n\nComo? Veja, tenho algumas sugestões:\n\n1. Quando algo está na moda, tem muita gente falando. Tem muito material sendo criado. Muita palestra sendo dada. Todo esse material de referência é um tesouro de valor inestimável para quem quer saber de fato o que está acontecendo;\n2. Quando tem muita gente tentando algo novo, tem muita gente enfrentando erros, dificuldades, vendo coisas que não funcionam. É aqui que profissionais inteligentes mais tiram proveito: aprendendo com os erros dos outros;\n3. Se houver muitos relatos de sucesso sobre uma determinada hype, preste atenção aos cenários onde esses sucessos ocorreram. São parecidos com o seu? Resolvem algum problema que você tem? Se não se aplicam hoje, podem se aplicar a algo que você estava planejando para o futuro;\n4. Junte tudo isso (material coletado, casos de fracasso e casos de sucesso) e faça sua própria análise. Analise também junto com colegas de trabalho. Façam simulações e provas de conceito. Discutam, criem conteúdo sobre o assunto.\n\nSe você seguir esses passos, dificilmente será iludido pela próxima hype que surgir. E se ela se provar mais do que uma nova \"modinha\", quem sabe você não sairá na frente como um case de sucesso?"
  },
  {
    "path": "manuscript/chapter_10.md",
    "content": "# Destrinchando performance de aplicações {#chapter_10}\n\n## Introdução e conceitos\n\nMuitas empresas utilizam a abordagem \"deixe o desempenho para depois\" para tratar com possíveis problemas de performance que possam ocorrer no ambiente de produção para a fase de testes. Na fase de testes, é provável que não se leve em consideração a carga que a aplicação possa sofrer e o tempo de resposta esperado, pois isso não foi especificado nos requisitos não funcionais - requisitos como o tempo de resposta de uma determinada operação ou a quantidade de acessos simultâneos. Além disso, temos o desafio de conciliar uma arquitetura evolutiva juntamente com a engenharia de desempenho ou performance. Na arquitetura evolutiva, temos que ser ágeis e permitir constantes melhorias na arquitetura, deixando as decisões para última hora, enquanto a engenharia de desempenho se preocupa em identificar possíveis gargalos e definir as tecnologias utilizadas já no início do projeto.\n\nAntes de irmos mais a fundo, vamos rever alguns conceitos importantes sobre desempenho de aplicações.\n\n**Throughput**: É a vazão ou taxa de transferência que sua aplicação consegue dar às requisições. Número de requisições por segundo/minuto ou hora. Quanto maior esse número, melhor. Geralmente, nas ferramentas de teste de carga, o Throughput é medido da seguinte forma: Throughput = (número de requisições) / (tempo total).\n\n**Latência**: É o tempo que leva para um pacote de dados ir de um ponto a outro. Ao contrário do Throughput, quanto maior a latência, pior a performance. Em sistemas distribuídos, este pode ser um grande problema.\n\nSistemas de alta performance têm como requisitos alta confiabilidade, baixa latência e alta taxa de vazão. Isso parece comum para quem vai desenvolver um serviço, porém não é uma tarefa das mais fáceis manter e melhorar a performance à medida que o sistema vai sendo utilizado, porque novas funcionalidades são adicionadas, a base de dados cresce.\n\n## Agilidade versus Performance\n\nSe a performance é a prioridade no projeto, considere verificar as dependências que está utilizando ou que irá utilizar. Ao utilizar uma biblioteca para substituir alguma parte de código, podemos ter a sensação de que estamos produzindo menos código, código limpo. Quanto menos código, menos bugs, certo? Nem sempre essa afirmação é correta. Você pode estar utilizando uma biblioteca que possui bugs também. Muitas pessoas desenvolvedoras acabam não acompanhando a evolução da linguagem e ficam \"amarradas\" a certas bibliotecas que utilizavam em versões passadas e que, na nova versão da linguagem, não se fazem mais necessárias. Bibliotecas podem trazer agilidade no desenvolvimento, mas será que elas degradam a performance ou deixam sua aplicação mais pesada? Avalie os prós e os contras de cada dependência que irá utilizar no projeto. Entenda o seu funcionamento e busque pelo histórico de bugs e vulnerabilidades.\n\n### Verifique o que está carregando na bagagem\n\nEm um projeto Java utilizando o Maven, por exemplo, podemos verificar a árvore de dependências utilizando o comando `mvn dependency:tree`, e ainda podemos filtrar somente por dependências de compilação, ex: `mvn dependency:tree -Dscope=compile`. \n\n``` asciidoc\n [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ testcontainers ---\n [INFO] com.testcontainers:jar:1.0-SNAPSHOT\n [INFO] \\- mysql:mysql-connector-java:jar:5.1.47:compile`\n```\n\nÉ muito comum desenvolvermos serviços que são empacotados em um arquivo jar conhecido como _fatjar_, em que todas as dependências são colocadas dentro do jar. Em muitos casos você não precisará daquela dependência para rodar o seu serviço. Ela pode ser uma dependência de teste, ou estar provida por um servidor de aplicações. Então é sempre uma boa ideia verificar as dependências do seu projeto.\n\nCuidado com a performance também quer dizer, em muitos casos, implementar mais código em vez de utilizar alguma biblioteca que facilita o trabalho e acelera o desenvolvimento, o que pode lhe custar a performance lá na frente.     \n\n## Como medir a performance? \n\nExistem muitas formas de medir a performance da aplicação, seja com um monitoramento em tempo real, seja com um teste de ‘stress’ antes de liberar uma funcionalidade em produção. Tudo vai depender do requisito não funcional solicitado. É claro que todo(a) usuário(a) quer ter sempre a resposta o mais rápido possível. Mas qual o limite aceitável? Essa é a medida que se deve ter em mente até mesmo antes de iniciar a codificação. Por exemplo: o tempo de login não pode ser superior a um segundo. Como posso medir isso?\nLembrando que não é simplesmente colocar que o 'login precisa ser feito em menos de um segundo'. Deve-se avaliar em quais circunstâncias esse login pode ou não demorar mais. Até onde o sistema de login pode escalar. Uma boa diretriz de média seria dizer: o tempo de resposta do login é de 1 segundo para 500 solicitações simultâneas, com uma carga de CPU de 60% e uma utilização de memória de 80%.\n\n### Capturando o tempo da requisição\n\nNeste capítulo, vamos utilizar a ferramenta [jMeter](https://jmeter.apache.org/), muito utilizada para criar diversos tipos de teste de carga e medir o desempenho. O objetivo aqui não é ser um tutorial do jMeter, mas mostrar como é possível gerar e visualizar dados através dele. Abaixo, um exemplo simples de medição de tempo de login considerando 10 usuários(as):\n\n![Imagem 10_01: Teste com JMeter que realiza a invocação de API de login, enviando usuário e senha via POST.](images/chapter_10_01.png)\n\n{pagebreak}\n\nAo rodar o teste acima, verificamos no Relatório de Sumário os resultados:\n\n![Imagem 10_02: Resultado do teste de invocação de API de login.](images/chapter_10_02.png)\n\nAlguns dados importantes neste momento:\n\n**Amostras:** Número de requisições realizadas.\n\n**Média:** Número médio de tempo de todas as requisições.\n\n**Min:** Menor tempo dentre todas as requisições.\n\n**Max:** Maior tempo dentre todas as requisições.\n\nAinda podemos ter gráficos mais ricos, utilizando o plugin [PerfMon](https://jmeter-plugins.org/wiki/PerfMon/), por exemplo:\n\n![Imagem 10_03: Utilização de plugin para análise de resultados com gráficos. ](images/chapter_10_03.png)\n\nNo gráfico acima, podemos ver que a maioria das requisições ficou entre 600 e 700 milissegundos em um cenário de testes com 1000 requisições.\nPodemos ter gráficos ainda mais bonitos e em tempo real, podendo utilizar o [grafana](https://grafana.com/) como visualizador de gráficos.\n\nVeja que capturamos o tempo total de um processo de login, porém se o login não está em um tempo adequado ou queremos melhorar ainda mais o tempo, precisamos visualizar cada componente em separado. Compreender como um valor é calculado e o que isso significa é essencial para tirar as conclusões corretas. Para esse fim, devemos examinar os métodos estatísticos usados para calcular e agregar dados de desempenho. Nunca utilize somente o valor da **média** para tirar conclusões sobre performance, pois durante um período de 24 horas com  milhares de requisições, os valores de pico serão ocultados pela média.\n\n## Entendendo e separando os componentes\n\nMedimos o tempo total de um login e precisamos melhorar o tempo de resposta. Para isso, precisamos testar separadamente cada componente da arquitetura para descobrir onde podemos diminuir o tempo. É possível que, com apenas uma ferramenta, não se possa medir a performance da sua aplicação. É provável que você utilize uma ferramenta de carga para estressar a aplicação e várias outras para coletar os dados. Como exemplo, podemos ter uma aplicação que tem uma api para o login com acesso ao banco de dados. No entanto, podemos ter cenários bem mais complexos. A imagem abaixo é uma representação da arquitetura para servir milhões de usuários(as):\n\n![Imagem 10_04: Arquitetura de uma aplicação complexa com capacidade para atender um alto número de requisições.](images/chapter_10_04.png)\n\n**Créditos:** <https://github.com/donnemartin/system-design-primer/blob/master/solutions/system_design/scaling_aws/>\n\nNo entanto, não foi de primeira que esta arquitetura foi definida. Foram muitos experimentos, testes e medições para chegar a uma arquitetura escalável. Deve ser possível medir a performance da réplica de leitura do banco de dados MySQL separadamente, por exemplo.\n\n## Monitorando a performance por componente\n\nConforme demonstrado acima, não é de primeira que se define uma arquitetura para milhões de usuários(as). É necessário estressar e medir para verificar onde estão os pontos que podem sofrer carga. Existem várias opções que mostram onde estão os gargalos da aplicação. Uma das várias opções é o [JavaMelody](https://github.com/javamelody), que pode ser utilizado em modo standalone junto com a sua aplicação Java, é free e muito simples de colocar na aplicação.\n\n![Imagem 10_05: Exibição de métricas para queries SQL executadas, através do JavaMelody](images/chapter_10_05.png)\n\nNa imagem acima, podemos notar que uma das consultas SQL demorou, em média, mais que o normal em relação a outras. Podemos descobrir de onde veio esse comando SQL, como também executar o comando SQL em modo 'Explain', a fim de revelar que a query está fazendo um 'full scan' e que será preciso ajustar a query ou criar índices específicos na tabela.\n\n![](images/chapter_10_06.png)\n\nNo outro exemplo abaixo, podemos ver um desvio bem grande no método 'findById', que, por sua vez, não utiliza um banco de dados MySQL, mas sim uma outra fonte de dados externo. Com essas informações em mãos, já é possível analisar de modo isolado cada comportamento.\n\n![Imagem 10_07: Repare o longo tempo de execução do método CompanyService.findByid que impacta diretamente no tempo de resposta da requisição ao servidor Spring.](images/chapter_10_07.png)\n\nExistem muitas ferramentas de monitoramento, e o que fica aqui como exemplo é que, em alguns casos, você vai precisar ir no detalhe e fazer algum ajuste fino na infraestrutura, ou até mesmo no código.\n\n## Monitorando a performance em sistemas distribuídos\n\nSistemas distribuídos, atualmente mais populares com a utilização de microsserviços, são complexos e difíceis de monitorar a performance. Nesse caso, vamos precisar de mecanismos mais sofisticados, como a utilização de um 'tracing' distribuído por exemplo. Aqui também existem várias soluções, como os famosos APMs, tais como o New Relic, AppDynamics, DataDog e Dynatrace. Vale lembrar que muitos provedores de cloud fornecem ferramentas de análise de performance, tal como o AWS Performance Insights.\nNo mundo OpenSource, vale destacar a ferramenta [Jaeger Tracing](https://www.jaegertracing.io/), cuja especialidade é fazer o monitoramento de serviços distribuídos rodando em uma infraestrutura do Kubernetes, por exemplo.\n\n![Imagem 10_08: Arquitetura de serviços distribuídos e a relação entre rastro e alcance nestes serviços.](images/chapter_10_08.png)\n\nPodemos observar em qual dos serviços o tempo de resposta não está adequado e tomar as devidas ações.\n\n## Mapeamento Objeto Relacional\n\nNo mundo Java, utilizamos o JPA para trabalhar com a camada de persistência em bancos de dados relacionais. Neste tópico, abordaremos algumas boas práticas para obtermos uma boa performance na utilização desse padrão.\n\n### Estratégia de geração de IDs\n\nO gerador de identificador TABLE é muito ineficiente. Ele é mais genérico e portável para a maioria dos bancos de dados, porém requer uma transação de banco de dados separada, como também uma conexão separada para garantir que o processo de geração do identificador não esteja vinculado à transação que iniciou. Emprega o uso de bloqueios no nível de linha que são pesados em comparação com as usadas pelas estratégias de geração de identificador como IDENTITY ou SEQUENCE.\nPortanto, se o banco de dados suportar sequences, é muito mais eficiente usar a estratégia SEQUENCE.\n\n### Enums\n\nGeralmente mapeamos **Enums** como String para facilitar a leitura no banco de dados ou exibir diretamente o literal do enum. \n\n```java\n@Enumerated(EnumType.STRING)\n@Column(length = 9)\nprivate TipoTelefoneEnum tipo;\n\npublic enum TipoTelefoneEnum {\n    CASA,\n    COMERCIAL,\n    CELULAR;\n}\n```\nPor mais legível que isso possa ser para a pessoa desenvolvedora, essa coluna ocupa muito mais espaço do que o necessário. Nesse caso, a coluna de tipo ocupa 9 bytes. Se armazenarmos 10 milhões de registros, apenas a coluna tipo de telefone ocupará 90 MB.\n\n#### Mapeando como inteiro\n\nObserve que a coluna @Enumerated não precisa receber o valor ORDINAL EnumType, pois é usado por padrão. Também estamos usando o tipo de coluna número inteiro smallint, pois é improvável que precisemos de mais de 2 bytes para armazenar todos os valores do tipo Enum.\n\n```java\n@Enumerated\n@Column(columnDefinition = \"smallint\")\nprivate TipoTelefoneEnum tipo;\n```\n\nO valor será armazenado como inteiro, iniciando com zero para o tipo CASA. Agora, isso é muito mais eficiente, mas menos expressivo. Então, como podemos ter desempenho e legibilidade?\n\nBasta criarmos uma tabela no banco de dados representando o enum e, na consulta, fazer o join com a tabela de constantes.\nParece trabalhoso? Mas pode valer a pena, se tivermos milhões de registros.\n\nÉ claro que existem os contras dessa solução. Se o enum for alterado, mudado de ordem ou acrescido de novos valores, os registros da base de dados terão que ser ajustados também.\n\nPortanto, é tudo uma questão de escolha! Então, escolha sabiamente.\n\n### Outras medidas de melhoria\n\nMuitas outras medidas de performance podem ser adotadas ou verificadas no seu código. Abaixo, alguns materiais, em inglês, para auxiliar:\n\n[Associação unidirecional ou bidirecional](https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/)\n\n[Problema de consulta N + 1](https://vladmihalcea.com/how-to-detect-the-n-plus-one-query-problem-during-testing/)\n\n**Referências:** <https://vladmihalcea.com/tutorials/hibernate/>\n\n## Conclusão\n\nNão tente resolver todos os problemas ao mesmo tempo. Comece construindo uma lista dos cinco principais contribuidores da hora e da queima da CPU, memória ou IO e explore soluções. Ataque um dos problemas e reavalie a arquitetura. Abaixo, algumas etapas que podem ajudar a encontrar e solucionar um problema de performance.\n\n**Descobrir:** Por que este ponto está com baixa performance?\n\n**Entender:** O que está causando a baixa performance?\n\n**Corrigir ou Melhorar:** Oportunidade de corrigir ou melhorar com base nos dados obtidos nas etapas acima.\n\n{pagebreak}\n\n"
  },
  {
    "path": "manuscript/resources/readme.md",
    "content": ""
  }
]