Arquitetando com Buzzwords Fabiano Guizellini Modos Arquiteto de Software na HBSIS @fmodos
Arquitetando com BuzzwordsFabiano Guizellini Modos
Arquiteto de Software na HBSIS
@fmodos
Por que Arquitetando com Buzzwords?
Contextualização
• Objetivo do sistemaInformatizar mais de 600 planilhas Excel
• Tamanho do sistema atualmente~200 tipos de entidades (tabela)~600 relacionamentos entre as tabelas~3000 colunas (~1500 colunas são fórmulas customizáveis)~1 tela de cadastro por entidade (~200 telas)
• Usuários podem Adicionar, Editar e Excluir registros
Cache
Qual a vantagem?
Acesso rápido a dados e economia de CPU
Qual a desvantagem?
Risco de acessar dados inconsistentes/desatualizados
Gânglio Basal – cache do cérebro
Local Cache Shared Cache
Como lidar com Cache em serviços distribuídos?
Menos concorrênciaCache menorRisco maior de dado desatualizado
Mais fácil para manter consistênciaDifícil identificar quem é o DONO da informação
Onde utilizamos Local Cache?
Fórmula modo edição:
Fórmula traduzida para o usuário:
Como mantemos dados consistentes usando Local Cache?
Publish Subscribe Message Pattern
Fonte: http://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html
Como utilizamos PubSub para atualizar LocalCache?
Publisher
Subscriber
Subscriber
CRUD -> C(reate) R(ead) U(pdate) D(delete)@Path("/cadeira")public class CadeiraRest {
@InjectCadeiraService cadeiraService;
@POSTpublic Response novaCadeira(CadeiraVO cadeiraVo) {
return Response.status(Status.CREATED).entity(cadeiraService.save(cadeiraVo)).build(); }
@GETpublic Response buscarTodasCadeiras() {
return Response.status(Status.OK).entity(cadeiraService.findAll()).build();}
@PUTpublic Response alterarCadeira(CadeiraVO cadeiraVo) {
return Response.status(Status.OK).entity(cadeiraService.alterarCadeira(cadeiraVo)).build();}
@DELETE@Path("{id}")public Response excluirCadeira(@PathParam("id")Integer id) {
cadeiraService.excluirCadeira(id);return Response.status(204).build();
}}
@Servicepublic class CadeiraService {
@InjectCadeiraRepository cadeiraRepository;
public Cadeira novaCadeira(CadeiraVO cadeiraVo) {return cadeiraRepository.insert(Cadeira.of(cadeiraVo));
}
public List<Cadeira> findAll() {return cadeiraRepository.findAll();
}
public Cadeira alterarCadeira(CadeiraVO cadeiraVo) {return cadeiraRepository.save(Cadeira.of(cadeiraVo));
}
public void excluirCadeira(@PathParam("id")Integer id) {cadeiraRepository.delete(id);
}}
CQRS -> Command Query Resource SegregationA forma que tu grava o dado é diferente da forma que tu lê.
http://www.kennybastani.com/2017/01/building-event-driven-microservices.html
Pontos Ken Pontos Modos
100 50
Source Target Golpe
Ken Modos Haduken
Source Target Golpe Pontos Data/Hora
Ken Modos Haduken -50 03-05-2017 00:47:00
Command
Event
Snapshot
Command -> uma intenção
Event -> algo que ocorreu
Snapshot -> salva o valor atual do
agregado
CQRS - Exemplos de código
• https://github.com/sdaschner/scalable-coffee-shop
• https://github.com/cer/event-sourcing-examples
• https://github.com/gregoryyoung/m-r
To CRUD or to CQRS?
Como utilizamos CQRS
ChangeRequestCommand:
{
modelId : ‘123’,
table : ‘cadeira’,
field : ‘valor’,
value : ‘50’
}
Command
Event Publica
Calcula Fórmulas
Atualiza Valor Fórmulas
Event SourcingLog de todas operações executadas em um aggregate
public void testEventSourcing(){
long fightID = 123;
Fight fight = fightService.getById(fightID);
Fight tempFight = new Fight():
Assert.assertNotEquals(fight.getPointsPlayer1(), tempFight.getPointsPlayer1());
Assert.assertNotEquals(fight.getPointsPlayer2(), tempFight.getPointsPlayer2());
List<FightEvent> fightEvents = fightEventService.getEventsByFightID(fightID);
for(FightEvent event : fightEvents){
tempFight.apply(FightCommand.of(event));
}
Assert.assertEquals(fight.getPointsPlayer1(), tempFight.getPointsPlayer1());
Assert.assertEquals(fight.getPointsPlayer2(), tempFight.getPointsPlayer2());
}
SQL NOSQL
Diagrama Classe Diagrama Relacional Diagrama NoSQL
-Deve permitir cadastro de Dish informando nome, descrição e lista de ingredientes. -Deve permitir que usuários logados através do email e senha insiram comentários em um Dish.
Estudo de Caso
Consulta de Dados
SQL NoSQL
select d.name, d.description from dish d where id=1
select i.name from ingredient i where i.dish_id=1
select c.text, u.nickname, u.name from comment c, useru where c.user_id = u.id and c.dish_id = 1
db.getCollection(‘Dish’).find({_id : 1})
Inconsistência de DadosO que acontece se o usuário “JOSÉ” atualizar o nickname para “JOÃO”?
SQL NoSQL
update user set nickname = ‘JOÃO’ where id=1 db.getCollection(‘User’).update({_id : 1}, {$set : {nickname : ‘JOÃO’}})
{name: “Dish Test”
ingredients : [“arroz”],
comments : Array[
{
text : ‘Comentario do José’
user : {
nickname : ‘JOSÉ’
}
]
Como resolvemos?
-Utilizando o Visitor Design Pattern que veremos nos próximos capítulos
Concorrência de alteração
SQL NoSQL
public void addComment(Dish dish, User user, String text){Comment comment = Comment.of(dish, user, text);commentRepository.insert(dish);
}
public void addComment(Dish dish, User user, String text){Comment comment = Comment.of(user, text);
dish.addComment(comment);dishRepository.save(dish);
}
• Syncronized??? Please nosyncronized(dish.getId(){
Comment comment = Comment.of(user, text);dish.addComment(comment);dishRepository.save(dish);
}
• Codigo com update do mongodbmongoTemplate.updateFirst(queryId(dish.getId()), new Update().addToSet(comment), “Dish”)
• Fila de processamento
Como resolvemos?
-Devido uma regra de negócio toda alteração somente pode ser atualizada na planilha em datas pré definidas, dessa forma possuímos serviços agendados que atualizam os Snapshots(Planilha) de forma síncrona.
Concorrência de alteração
Controle Transação MongoDB???
“Aquilo que você conhece não pode te machucar” – Livro: A lógica do cisne negro
Como resolvemos?
-Agrupamos todas as informações em um registro
-Processos batch, temos um controle de execução que gera log de cada step e consegue continuar de onde parou
Como alterar registros de um agregado não estruturado?
Visitor Design Pattern
class ApplyChangeVisitor implements Visitor{ChangeRequestCommand change = ...
public void visit(Entidade e){if(e.getId().equals(change.getModelId()){
e.setFieldValue(change.getField(), change.getValue());}
}}
class abstract Entidade {public void accept(Visitor visitor){
visitor.visit(this);//busca associações e invoca accept
refEntidade.accept(visitor);}
}
Entidade planilha = ....ChangeRequestCommand change = ....planilha.accept(new ApplyChangeVisitor(change));
Editing/Publishing Pattern
https://martinfowler.com/articles/two-stack-cms/#edit-publish
Como utilizamos Editing/Publishing
• Criamos um conceito de View por cada usuário
• Comandos de alteração são armazenados no Editing da View do usuário
• Publishing é a versão aprovada compartilhada por todos usuários
View Usuário = Comandos Editing + Versão Publishing
"My feeling is that when we prepare a program, it can be like composing poetry or music; as Andrei Ershov has said [9], programming can give us both intellectual and emotional satisfaction, because it is a real achievement to master complexity and to establish a system of consistent rules." Computer Programming as an Art Donald Knuth, 1974
Obrigado!@fmodos
Referências
• http://www.kennybastani.com/2017/01/building-event-driven-microservices.html
• http://cqrs.nu/Faq
• http://www.enterpriseintegrationpatterns.com/ramblings/eip1_examples_updated.html
• https://martinfowler.com/articles/201701-event-driven.html
• https://pt.wikipedia.org/wiki/Visitor_Pattern
• https://twitter.com/gregyoung
• https://docs.mongodb.com/manual/
• https://martinfowler.com/articles/two-stack-cms/#edit-publish