"De onde crescem as pernas" ou o que precede a programação?

Olá a todos! Outro dia, como parte da plataforma educacional OTUS, é lançado um novo curso: “Architecture and Design Patterns” . Em conexão com o início, realizamos uma lição aberta tradicional . Ele estudou os recursos de um aplicativo monolítico, arquiteturas de vários níveis e sem servidor. Examinamos em detalhes o sistema orientado a eventos, o sistema orientado a serviços e a arquitetura de microsserviços.



O professor é Matvey Kalinin , especialista com mais de 20 anos de experiência em programação e autor do curso “Architecture and Design Patterns”.

Um pouco de fundo


Inicialmente, os programas realmente resolveram a tarefa definida por si mesmos e foram bastante isolados. Mas com o tempo, os programas cresceram e as pessoas começaram a entender que a complexidade emergente do funcional começa a afetar a velocidade das melhorias, a confiabilidade e a resistência a vários tipos de complicações.

De fato, quando temos um ou dois programas iguais e não mudamos, escrever esses programas e garantir sua interação não é difícil. Porém, quando existem mais e mais deles, os problemas não podem ser evitados e não importa que tipo de pacote de software esteja envolvido.

Hoje, as aplicações, na maioria das vezes, são distribuídas. Eles consistem em vários módulos e são interconectados por mensagens do sistema. Ou seja, é obtido um conglomerado bastante grande de programas que interagem entre si. E para que eles interajam com sucesso, precisamos considerar:

  • resposta rápida;
  • largura de banda
  • desempenho em termos de uma unidade de recursos;
  • capacidade de escalar;
  • recursos de integração;
  • características das plataformas usadas;
  • características dos processos de negócios existentes;
  • e muito mais…

Ao mesmo tempo, não se pode deixar de lembrar a seguinte citação:

“O código correto do programa não exige grandes custos de mão-de-obra para sua criação e manutenção. As alterações são feitas de maneira rápida e fácil. Erros são poucos. Os custos de mão-de-obra são mínimos, enquanto a funcionalidade e a flexibilidade são máximas .

Robert Cecil Martin

Ou seja, ao escrever o programa idealmente uma vez (se estiver bem escrito), as melhorias serão mínimas. Como conseguir isso e vincular um e outro? Para responder a essa pergunta, passamos à história.


?


Então, percebemos que precisamos estruturar adequadamente o produto de software. Mas qual é o objetivo da arquitetura de software? De qualquer forma, por que usamos a frase “pernas crescem” no título deste artigo?

O fato é que, quando você começa a programar, já sabe o que deseja construir. Inicialmente, tudo começa com um processo de negócios. Há um pedido e um cliente que descrevem (pelo menos em palavras) como seu sistema deve funcionar. E, a princípio, esse sistema existe apenas na forma descrita por ele. Então, esse processo é formalizado e delineado, mas isso ainda é metade da batalha, porque um maior desenvolvimento começa. E o desenvolvedor precisa converter esse processo em um produto de software, o que deve ...

Então é hora de lembrar outra citação:

"O objetivo da arquitetura de software é reduzir o trabalho humano necessário para criar e manter um sistema".

Robert Cecil Martin


Não deve surpreender que o objetivo seja formado por tais frases gerais. O fato é que a arquitetura vive em idéias abstratas. Por quê? Porque a pessoa envolvida na arquitetura de software transforma a visão de um cliente comercial na visão de um desenvolvedor . E se estamos falando do arquiteto da equipe de desenvolvimento e do arquiteto Enterprise, cada um deles tem um objetivo diferente, mas ambos lutam por uma coisa : reduzir os custos de mão-de-obra humana.

Nesse contexto, é interessante observar os valores do software:

  • O código atende aos requisitos do processo de negócios;
  • existe a possibilidade de uma rápida mudança de comportamento.

E aqui está a pergunta: por que é mais importante a operação do sistema ou a simplicidade de alterá-lo ? Para responder, vejamos o programa do ponto de vista da utilidade:

se houver um programa funcionando corretamente que não permita alterações, esse programa acabará se tornando irrelevante - quando os requisitos forem alterados.

Se o programa não funcionar corretamente, mas puderem ser feitas alterações, ele poderá funcionar corretamente dentro da estrutura de requisitos em mudança. Este programa permanecerá útil sempre.

Paradigmas (modelos) de programação


O que você acha que é o mais famoso (primeiro) modelo de programação? Claro, um monólito . É apropriado aqui novamente, por um momento, voltar a 1968 e lembrar Edsger Dijkstra, que mostrou que o uso descontrolado de transições (instruções goto) é prejudicial à estrutura do programa. Ele sugeriu substituir as transições por construções mais compreensíveis se / então / outra e fazer / enquanto / até.

Agora, as instruções goto podem ser vistas com menos frequência. Mas antes, as instruções goto eram muito comuns. Em geral, essa é uma forma do mal, porque quando você vê o código no qual há uma instrução goto, tem a sensação de que talvez não encontre exatamente o ponto para onde vai. E quanto mais for, mais complicado será o código do espaguete. Agora chamamos esse código de "espaguete", onde, por exemplo, 20 ifs aninhados e, possivelmente, um goto. Este também não é um código muito claro. Imagine que você tenha de 10 a 15 e está tentando entender como o ciclo funciona - era isso que Dijkstra tinha em mente.

O código espaguete é um programa mal estruturado, confuso e difícil de entender que contém muitas instruções goto (especialmente saltos), exceções e outras construções que degradam a estrutura. Em geral, um antipadrão bem conhecido e bastante comum de programação. Esse programa leva muito tempo para entender, dar suporte e testar.

Programação estrutural


Havia linguagens de programação, eles alcançaram alguns objetivos. Até certo ponto, a estrutura dos programas não afetou muito a implementação. Mas os programas cresceram, entre eles numerosos laços se formaram. E, a certa altura, uma pessoa sentiu que era importante compactar o algoritmo em uma estrutura que fosse fácil de ler, testar. As mudanças começaram no nível da estrutura do programa. Ou seja, não apenas o próprio programa teve que satisfazer o resultado, mas a estrutura do programa também teve que atender a algum critério.

Assim, mudamos suavemente para a programação estrutural . Segundo ele, o programa é construído sem o uso do operador goto e consiste em três estruturas básicas de controle:

  • seqüência,
  • ramificação
  • ciclo.

São utilizados subprogramas, o desenvolvimento em si é realizado passo a passo, de cima para baixo.
E novamente, em 1966 ... Este ano, Ole-Johan Dahl e Kristen Nyugor notaram que na linguagem ALGOL é possível mover o quadro da pilha de chamadas de função para a memória dinâmica (heap), para que as variáveis ​​locais declaradas dentro da função possam ser salvas após sair dela. Como resultado, a função se transformou no construtor da classe, variáveis ​​locais em variáveis ​​de instância e funções aninhadas em métodos. Isso levou à descoberta do polimorfismo através do uso estrito de indicadores de função.

Programação orientada a objetos


Como todos sabem, no OOP, os programas são representados como uma coleção de objetos, cada um dos quais é uma instância de uma classe específica, e as classes formam uma hierarquia de herança.

Princípios básicos de estruturação:

  • abstração;
  • herança;
  • polimorfismo.

Você pode olhar para todos esses princípios de outra perspectiva. Robert Martin desenvolveu os princípios do SOLID, que, por um lado, determinam como um programador trabalha com abstrações e, por outro lado, formam o processo de polimorfismo, herança.

Programação imperativa


Um programa imperativo é semelhante às ordens que um computador deve executar. Tais programas são caracterizados por:

  • as instruções são escritas no código fonte do programa;
  • as instruções devem ser seguidas sequencialmente;
  • os dados obtidos durante a execução das instruções anteriores podem ser lidos da memória por instruções subsequentes;
  • os dados obtidos executando a instrução podem ser gravados na memória.

Também um design muito antigo. As principais características das linguagens imperativas:

  • uso de variáveis ​​nomeadas;
  • uso de operador de atribuição;
  • uso de expressões compostas;
  • uso de rotinas.

No entanto, continuamos a "viagem no tempo". Desta vez, retornaremos por um momento em 1936 (!). É interessante que neste ano a Alonzo Church tenha inventado o cálculo lambda (ou λ-calculus), que mais tarde, em 1958, formou a base da linguagem LISP inventada por John McCarthy. O conceito fundamental do cálculo λ é imutabilidade - isto é, a impossibilidade de alterar os valores dos símbolos.

Programação funcional


A programação funcional envolve o cálculo dos resultados das funções a partir dos dados de origem e os resultados de outras funções e não implica armazenamento explícito do estado do programa.

De fato, isso significa que uma linguagem funcional não possui uma declaração de atribuição.

Vejamos a diferença entre estilos imperativos e funcionais usando um exemplo:

#  
target = []  #   
for item in source_list:  #     
    trans1 = G(item)  #   G()
    trans2 = F(trans1)  #   F()
    target.append(trans2)  #     
#  
compose2 = lambda A, B: lambda x: A(B(x))
target = map(compose2(F, G), source_list)

Então, o que é arquitetura de software?


Arquitetura de software é um conjunto de decisões sobre a organização de um sistema de software.

Inclui:

  • seleção de elementos estruturais e suas interfaces;
  • o comportamento dos elementos e interfaces selecionados, sua interação;
  • combinar elementos selecionados de estrutura e comportamento em sistemas maiores;
  • estilo arquitetônico que guia toda a organização.

Observe: primeiro chegamos à conclusão de que irmos não nos convinha, depois vimos que existem certas regras (encapsulamento, herança, polimorfismo), depois percebemos que essas regras não funcionam apenas, mas de acordo com certos princípios. O quarto ponto é o estilo arquitetônico, e falaremos sobre isso mais tarde.

O principal objetivo da arquitetura é oferecer suporte ao ciclo de vida do sistema. Uma boa arquitetura facilita o aprendizado do sistema, o desenvolvimento, a manutenção e a implantação. O objetivo final é minimizar os custos ao longo da vida útil do sistema e maximizar a produtividade do programador e, mais precisamente, da equipe de desenvolvimento.

No começo, conversamos sobre quais regras precisávamos para escrever programas. Mas, além de escrever programas, também há suporte, desenvolvimento e implantação. Ou seja, a arquitetura não captura uma determinada área da programação, mas todo o ciclo de desenvolvimento.

Uma boa arquitetura deve fornecer:

  1. Uma variedade de casos de uso e operação eficaz do sistema.
  2. Simplicidade de manutenção do sistema.
  3. Facilidade de design do sistema.
  4. Fácil implantação do sistema.

Estilos arquitetônicos


Monólito


Primeiro de tudo, vamos falar sobre o conhecido monólito . Esse estilo ainda é encontrado, no entanto, em pequenos sistemas. Arquitetura monolítica significa que seu aplicativo é um módulo grande e conectado. Todos os componentes foram projetados para trabalhar juntos, compartilhar memória e recursos. Todas as funções ou sua parte principal estão concentradas em um processo ou contêiner, dividido em camadas ou bibliotecas internas.



Vantagens:

  1. Fácil de implementar. Não há necessidade de perder tempo pensando em comunicação entre processos.
  2. Fácil de desenvolver testes de ponta a ponta.
  3. Fácil de implantar.
  4. Escale facilmente com o Loadbalancer em várias instâncias do seu aplicativo.
  5. Fácil de operar.



Mas agora ele tem mais deficiências :

  1. A forte coesão leva ao emaranhado com a evolução da aplicação.
  2. A escala independente de componentes leva a complicações e a um novo teste completo da funcionalidade.
  3. Mais difícil de entender.
  4. À medida que a complexidade aumenta, o tempo de desenvolvimento aumenta.
  5. Falta de isolamento de componentes.

Por um lado, o monólito é bom, mas assim que você começa a desenvolvê-lo, surgem dificuldades.

O que é um serviço?


Agora todo mundo sabe o que é um serviço. Pode ser definido como um recurso visível que executa uma tarefa repetitiva e é descrito por uma instrução externa.

Os serviços modernos têm os seguintes recursos :

  • os serviços são orientados não nos recursos de TI, mas nas necessidades de negócios;
  • os serviços são auto-suficientes e são descritos em termos de interfaces, operações, semântica, características dinâmicas, políticas e propriedades do serviço;
  • a reutilização de serviços é fornecida por seu planejamento modular;
  • os contratos de serviço são concluídos entre entidades chamadas fornecedores e usuários e não afetam a implementação dos próprios serviços;
  • durante seu ciclo de vida, os serviços são hospedados e tornados visíveis por meio de metadados, registros e repositórios de serviços;
  • agregação: a combinação de processos de negócios e aplicativos complexos para uma ou várias empresas se baseia em serviços pouco acoplados.

Como resultado dos recursos acima, surgiu o conceito de arquitetura orientada a serviços (SOA).

Arquitetura Orientada a Serviços (SOA)


SOA é um estilo de arquitetura para criar uma arquitetura de TI corporativa, usando os princípios de orientação a serviços para obter uma conexão estreita entre a empresa e seus sistemas de informações de suporte.



SOA possui as seguintes características :

  1. Melhora o relacionamento entre a arquitetura corporativa e os negócios.
  2. Permite criar aplicativos complexos a partir de conjuntos de serviços integrados.
  3. Cria processos de negócios flexíveis.
  4. Não depende de um conjunto de tecnologias.
  5. Autônomo no sentido de evolução e implantação independentes.

Um modelo de implantação de SOA consiste em inteligência e desenvolvimento de negócios e inteligência e desenvolvimento de TI. A montagem consiste em serviços de programação e criação de aplicativos complexos. A hospedagem consiste em hospedar aplicativos e ferramentas de tempo de execução, como Enterprise Service Buses (ESBs). Quanto ao manual , ele consiste em apoiar o ambiente operacional, monitorar o desempenho dos serviços e monitorar a conformidade com as políticas de serviço.



Arquitetura de microsserviço


É hora de falar sobre a arquitetura de microsserviços . Nele, o aplicativo consiste em pequenos aplicativos de serviço independentes, cada um com seus próprios recursos. Os serviços interagem entre si para executar tarefas relacionadas às oportunidades de negócios. Existem várias unidades de implantação. Cada serviço é implantado de forma independente.



Vantagens:

  1. Suporta modularidade de todo o sistema.
  2. Serviços não relacionados são mais fáceis de modificar para atender a diferentes aplicativos.
  3. Serviços diferentes podem pertencer a equipes diferentes.
  4. Os serviços de serviço podem ser reutilizados em toda a empresa.
  5. Mais fácil de entender e testar.
  6. Não está vinculado à tecnologia usada em outros serviços.
  7. O isolamento do serviço aumenta a confiabilidade geral de toda a funcionalidade.



Desvantagens:

  1. Dificuldades com a implementação da funcionalidade geral (registro, direitos de acesso etc.).
  2. É difícil realizar testes de sistema de ponta a ponta.
  3. Operação e suporte mais difíceis.
  4. É necessário mais equipamento do que para um monólito.
  5. O apoio de várias equipes leva à coordenação da interação entre elas.

Vale notar que nessa arquitetura é muito difícil fazer qualquer coisa sem o DevOps.

Arquitetura em camadas


Arquitetura em camadas é o padrão de arquitetura mais comum. Também é chamada arquitetura de n camadas, em que n é o número de níveis.

O sistema é dividido em níveis, cada um dos quais interage com apenas dois vizinhos.

A arquitetura não implica nenhum número obrigatório de níveis - pode haver três, quatro, cinco ou mais. Na maioria das vezes, sistemas de três camadas são usados: com um nível de apresentação (cliente), um nível lógico e um nível de dados.
As camadas mais comuns são :

  • camada de apresentação (para trabalhar com usuários);
  • camada de aplicação (serviço - segurança, acesso);
  • camada de lógica de negócios (implementação de domínio);
  • camada de acesso a dados (representação da interface para o banco de dados).

Camadas fechadas


O conceito de isolamento de níveis separa estritamente um nível do outro: você só pode ir de um nível para o próximo e não pode pular vários de uma vez.



Camadas abertas


O sistema permite saltar sobre os níveis abertos e cair sobre os localizados abaixo.



MVC


O conceito de MVC foi descrito em 1978. A versão final do conceito MVC foi publicada apenas em 1988 na revista Technology Object.

O objetivo principal é separar a lógica de negócios (modelo) de sua visualização (apresentação, exibição). O que isso dá:

  • A possibilidade de reutilização de código é aumentada.
  • o usuário pode ver os mesmos dados simultaneamente em diferentes contextos.



O modelo fornece dados e responde aos comandos do controlador, alterando seu estado. A visualização é responsável por exibir os dados do modelo para o usuário, respondendo às alterações do modelo. O controlador interpreta as ações do usuário, notificando o modelo da necessidade de alterações.



Arquitetura Orientada a Eventos


Outra arquitetura interessante. É usado no desenvolvimento e implementação de sistemas que transmitem eventos entre elementos de software fracamente acoplados.

Consiste em componentes de processamento de eventos de uso único diferentes que recebem e processam eventos de forma assíncrona.

O modelo consiste em duas topologias principais - um intermediário e um intermediário.

Topologia do revendedor


Existem processos em que é necessário controle sobre a sequência de etapas. Aqui somos intermediários úteis.

Os principais tipos de componentes de arquitetura:

  • filas de eventos;
  • mediador de eventos;
  • canais de eventos;
  • manipuladores de eventos.

Evento Um

evento pode ser definido como uma "mudança significativa de estado". Um evento pode consistir em duas partes:

  • cabeçalho (nome do evento, carimbo de data e hora do evento e tipo de evento);
  • body (descreve o que realmente aconteceu).

Topologia do mediador

O cliente envia um evento para a fila de eventos, que é usada para enviar o evento ao mediador.

O mediador recebe o evento inicial e envia eventos assíncronos adicionais aos canais de eventos para cada etapa do processo.

Os processadores de eventos, que ouvem os canais de eventos, recebem um evento de um intermediário e executam certa lógica de negócios processando o evento.



Topologia do intermediário

A topologia do intermediário difere da topologia intermediária, pois não há mediador central de eventos. O fluxo de mensagens é distribuído entre os componentes do processador de eventos em uma cadeia por meio de um intermediário de mensagens leve (por exemplo, ActiveMQ, HornetQ etc.). Essa topologia é útil quando há um fluxo relativamente simples de processamento de eventos e não há necessidade de uma orquestração centralizada de eventos.

Cada componente de um processador de eventos é responsável por processar um evento e publicar um novo evento indicando a ação que acabou de ser concluída.



Se a primeira situação era assíncrona "em algum lugar abaixo", a segunda situação é assíncrona, pode-se dizer, completamente. Um evento gera vários eventos e eles podem aumentar e aumentar.

Vantagens arquitetura orientada a eventos:

  • os componentes são isolados e permitem que cada um seja finalizado sem afetar o restante do sistema;
  • facilidade de implantação;
  • alta performance. Permite operações assíncronas paralelas;
  • escala bem.

Desvantagens:

  • difícil de testar;
  • difícil de desenvolver devido à assincronia pronunciada.

Arquitetura sem servidor


Essa é uma maneira de criar e executar aplicativos e serviços sem a necessidade de gerenciamento de infraestrutura. O aplicativo ainda é executado nos servidores, mas a plataforma assume completamente o controle desses servidores.

Toda a infraestrutura é suportada por fornecedores terceirizados e a funcionalidade necessária é oferecida na forma de serviços responsáveis ​​por processos de autenticação, passagem de mensagens etc.

A seguinte terminologia é diferenciada:

  1. (Function-as-a-Service) — , .
  2. (Backend as a Service) — , - API, . , , , .

Se considerarmos a arquitetura do “Cliente-servidor”, a maior parte da lógica do sistema (autenticação, navegação na página, pesquisa, transações) é implementada pelo aplicativo do servidor.



Na arquitetura sem servidor, tudo é um pouco diferente. A autenticação é substituída por um serviço BaaS de terceiros (serviço em nuvem pronto), o acesso ao banco de dados também é substituído por outro serviço BaaS. A lógica do aplicativo já está parcialmente dentro do cliente - por exemplo, rastreando a sessão de um usuário.

O cliente já está a caminho de se tornar um aplicativo de uma página. A pesquisa pode ser feita através do serviço de pesquisa (FaaS - funciona como um serviço). A função de compra também é isolada do cliente como um serviço separado (FaaS).



Bem, só isso, se você estiver interessado nos detalhes, assista ao vídeo inteiro. Quanto ao novo curso, você pode se familiarizar com o programa aqui.

All Articles