• Stars
    star
    1,147
  • Rank 40,664 (Top 0.9 %)
  • Language
  • License
    Apache License 2.0
  • Created over 4 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Proposta de Arquitetura Limpa para o Dart/Flutter

Clean Dart

Proposta de Arquitetura Limpa para o Dart/Flutter

Início

Podemos dizer que uma arquitetura limpa pode definir o futuro do seu projeto, sabendo disso, devemos tê-la como objeto de estudo constante para que assim saibamos onde, quando e como aplicá-la.

Para essa proposta nos baseamos nas camadas da Arquitetura Limpa proposta por Robert C. Martin no livro “Arquitetura Limpa: O Guia do Artesão para Estrutura e Design de Software”.

Camadas de uma Arquitetura Limpa

Robert C. Martin conclui que uma arquitetura deve conter pelo menos 4 camadas principais e independentes para ser considerada “limpa”, são elas:

  1. Regras de Negócio Corporativas
  2. Regras de Negócio da Aplicação
  3. Adaptadores de Interface
  4. Frameworks & Drivers (Externos)

Image 3

Regras de Negócio Corporativas

São as regras de negócio cruciais para a sua aplicação, são representadas por modelos de dados denominado "Entidades", essa camada tem as regras mais sensíveis de um sistema, por isso ela está no topo das camadas. Uma "Entidade" deve ser pura, ou seja, não deve conhecer nenhuma outra camada, porém é conhecida pelas outras camadas.

Regras de Negócio da Aplicação

São as regras que só o computador pode executar, aqui temos uma representação de comandos chamados de "Casos de Uso", e basicamente representam as ações que um usuário pode fazer na aplicação.

Um "Caso de Uso" conhece apenas as Entidades, porém não sabe nada sobre as implementações das camadas de mais baixo nível.

Se um "Caso de Uso" precisar acessar uma camada superior, deverá fazê-lo por meio de contratos definidos por uma interface, seguindo o “Princípio de Inversão de Dependências” do SOLID.

Adaptadores de Interface

Essa camada é responsável por “dar suporte” para as camadas mais altas (Regras de Negócios) convertendo os dados externos em um formato que cumpra os contratos de interface definidos pelas Regras de Negócios.

Frameworks & Drivers

Todas as abstrações feitas pelas camadas mais altas foram para aumentar a facilidade do plug & play dos artefatos externos como um banco de dados ou uma interface gráfica.

Essa camada normalmente sofre muitas modificações, porém com uma arquitetura limpa aplicada isso pode ser completamente indolor e segura para a sua regra de negócio.

Podemos então trocar uma API REST por outra em GraphQL sem afetar suas regras de negócio. Poderemos também trocar a interface gráfica completamente ou até mesmo trocar o Flutter pelo AngularDart e mesmo assim as Regras de Negócio ficarão funcionais!

Dado as descrições iremos apresentar a proposta de Arquitetura Limpa da Flutterando, a “Clean Dart”.

Clean Dart

Image 1

Usando o Flutter como exemplo teremos então quatro camadas mantendo a “Arquitetura de Plugin”, com foco principal no Domínio da Aplicação, camada esta que hospeda as 2 Regras de Negócio principais, estamos falando das Entidades e dos Casos de Uso.

Image 1

A proposta de Arquitetura se propõe a desacoplar as camadas mais externas e preservar a Regra de Negócio.

Presenter

A Camada Presenter fica responsável por declarar as entradas, saídas e interações da aplicação.

Usando o Flutter como exemplo, hospedaremos os Widgets, Pages e também Alguma Gerência de Estado, já no backend como exemplo, seria nesta camada onde colocaríamos os Handlers ou Commands da nossa API.

Domain

A camada de Domain hospedará as Regras de Negócio Corporativa(Entity) e da Aplicação(Usecase).

Nossas Entidades devem ser objetos simples podendo conter regras de validação dos seus dados por meio de funções ou ValueObjects. A Entidade não deve usar nenhum objeto das outras camadas.

Os Casos de Uso devem executar a lógica necessária para resolver o problema. Se o Caso de Uso precisar de algum acesso externo então esse acesso deve ser feito por meio de contratos de interface que serão implementados em uma camada de mais baixo nível.

A camada Domain deve ser responsável apenas pela execução da lógica de negócio, não deve haver implementações de outros objetos como Repositories ou Services dentro do Domain.

Tomando um Repository como exemplo, teremos que ter apenas o contrato de interfaces(Abstrações) e a responsabilidade de implementação desse objeto deverá ser repassado a outra camada mais baixa.

Infrastructure (Infra)

Esta camada dá suporte a camada Domain implementando suas interfaces. Para isso, adapta os dados externos para que possa cumprir os contratos do domínio.

Muito provavelmente nessa camada iremos implementar alguma interface de um Repository ou Services que pode ou não depender de dados externos como uma API ou acesso a algum Hardware como por exemplo Bluetooth.

Para que o Repository possa processar e adaptar os dados externos devemos criar contratos para esses serviços visando passar a responsabilidade de implementação para a camada mais baixa da nossa arquitetura.

Como sugestão, iremos criar objetos de DataSource quando quisermos acessar um dado externo, uma BaaS como Firebase ou um Cache Local usando SQLite por exemplo. Outra sugestão seria criar objetos denominados Drivers para interfacear a comunicação com algum Hardware do dispositivo.

Os acessos externos como Datasources e Drivers devem ser implementados por outra camada, ficando apenas os Contratos de Interface nesta camada de Infra.

External

Aqui começaremos a implementar os acessos externos e que dependem de um hardware, package ou acesso muito específico.

Basicamente a camada External deve conter tudo aquilo que terá grandes chances de ser alterado sem que o programador possa intervir diretamente no projeto.

No Flutter por exemplo, para cache local usamos o SharedPreferences, mas talvez em alguma estágio do projeto a implementação do SharedPreferences não seja mais suficiente para a aplicação e deve ser substituída por outro package como Hive, nesse ponto a única coisa que precisamos fazer é criar uma nova classe, implementando o Contrato esperado pela camada mais alta (que seria a Infra) e implementarmos a lógica usando o Hive.

Um outro exemplo prático seria pensar em um login com Firebase Auth, porém outro produto deseja utilizar um outro provider de autenticação. Bastaria apenas implementar um datasource baseado no outro provider e “Inverter a Dependência” substituindo a implementação do Firebase pela nova quando for necessário.

Os Datasources devem se preocupar apenas em “descobrir” os dados externos e enviar para a camada de Infra para serem tratados.

Da mesma forma os objetos Drivers devem apenas retornar as informações solicitadas sobre o Hardware do Device e não devem fazer tratamento fora ao que lhe foi solicitado no contrato.

Dicas

Pense por camada

Quando for desenvolver comece a pensar por camada, não devemos nos preocupar com o que tem na camada de Presenter ou External por exemplo. Se pensarmos nas camadas mais externas podemos acabar nos orientando (erroneamente) por essas camadas. Assim, devemos nos acostumar a desenvolver camada por camada, de dentro para fora e não ao contrário.

Talvez no começo da sua jornada "Limpa" algumas camadas possam parecer "sem utilidade", isso acontece quando nossa mente ainda não está Pensando em Camadas (ou porque sua Regra de Negócio é simples demais para isso)

Teste de Unidade será sua nova UI

É muito comum os desenvolvedores criarem primeiro as suas Views para que então possam "testar" as Regras de Negócio. Mas nós já temos uma ferramenta própria para isso e um lugar dedicado para armazenar esses testes.

Desenvolver de forma "limpa" está em total sinergia com o TDD(Test Driven Development) pois a camada de Presenter será uma das últimas coisas que iremos pensar no desenvolvimento da nossa feature.

Gaste mais tempo tratando erros

"É melhor deixar uma Exception acontecer do que tratar um erro de forma genérica"... Uma boa dica é usar alguma classe que nos obrigue a tratar os erros como o Either do pacote dartz.

Either é uma classe que pode receber dois tipos de dados, um Left (para quando enviar o erro) e o Right(para enviar o dado esperado). Isso também diminui muito a necessidade de realizar um tratamento manual de erro com try catch em camadas mais superiores como Presenter.

Não caia na tentação de furar uma camada

Algumas vezes você poderá ter um UseCase muito simples, que apenas repassa para o Repository, como por exemplo em um CRUD onde você apenas precisa validar se a informação está chegando da maneira correta e repassar para o Repository fazer seu trabalho.

Parece estranho você ter uma classe com um método que faz somente a validação dos dados e repassa para outra classe, porém você verá a grande utilidade disso no momento de uma manutenção. Pois muitas vezes o UseCase pode nascer pequeno mas em um futuro próximo ele pode ganhar corpo.

Um exemplo disso é a utilização do Firebase, o package do Firebase te retornar uma Stream que você pode muito bem colocar ele direto na sua View, porém se um dia você quiser remover o firebase do seu projeto, você terá que reconstruir toda sua tela ou pior todo seu projeto.

Sendo assim não caia na tentação de chamar o Repository direto do Controller ou mesmo plugar o Firebase direto na sua View, além de infringir as regras da arquitetura, você irá se arrepender em um futuro próximo.

Assine!

Apreciamos o seu feedback! Se concorda com a proposta de Arquitetura Limpa "Clean Dart", deixe uma Star neste repositório. Uma Star é o mesmo que assinar um "manifesto limpo" concordando com essa proposta.

Estamos abertos a sugestões e melhorias na documentação! Faça isso por meio das issues, nossa equipe ficará muito contente com seu interesse em melhorar essa ferramenta para a comunidade.

Sinta-se a vontade para abrir um PR com correções na documentação dessa proposta.

Exemplos

Links úteis

More Repositories

1

modular

A smart project structure
Dart
1,295
star
2

roadmap

Flutter roadmap pt-BR
1,215
star
3

slidy

CLI package manager and template for Flutter
Dart
804
star
4

hasura_connect

Connect your Flutter/Dart apps to Hasura simply.
Dart
204
star
5

forum

Organizando as discussões feitas no Telegram, Discord e Facebook no Github em formato de Issues.
173
star
6

triple_pattern

Segmented State Pattern for Reactive Property
Dart
156
star
7

yuno

Minimal Open-source Retrogame Frontend
Dart
113
star
8

asuka

Show Snackbars, dialogs, ModalSheets in a single provider. Simple and Clean.
Dart
83
star
9

uno

Future based HTTP client for the Dart and Flutter
Dart
77
star
10

dart_backend

Roadmap para aprender como utilizar Dart no backend
73
star
11

teddy_todo

Dart
65
star
12

calamidade

Dart
64
star
13

dartion

Json Server RESTful, Up your backend in 5 Seconds!
Dart
58
star
14

routefly

Folder-based route manager inspired by NextJS and created by the Flutterando community.
Dart
53
star
15

clean-dart-search-bloc

Github Search using "Clean Dart", BLoC and Modular.
C++
52
star
16

auto_injector

Dependency injection system. But without build_runner :)
Dart
52
star
17

rx_notifier

Extension to ValueNotifier by transparently applying functional reactive programming (TFRP)
Dart
42
star
18

asp

ASP (Atomic State Pattern) offers a simplified and modularized approach to state management for Flutter.
C++
40
star
19

minicore

Flutter/Dart Architecture proposal inspired by Clean Architecture.
40
star
20

semana-do-flutter-arc

Semana do Flutter de Maio de 2020
C++
36
star
21

music_player_app

A challenge made by Flutterando using Flutter Desktop
Dart
33
star
22

flutter_mobx_extension

TypeScript
32
star
23

result_dart

Result for dart. It is an implementation based on Kotlin Result and Swift Result.
Dart
32
star
24

nubank_layout

Desafio criado com os membros da Flutterando
Dart
32
star
25

flutter-coverage

VSCeode Extension for view the code coverage per folder/file in the test view
TypeScript
31
star
26

github_search

Github Search - Flutter Modular example
Dart
23
star
27

shelf_swagger_ui

Swagger UI plugin for Shelf
Dart
22
star
28

dson_adapter

Convert JSON to Dart Class withless code generate(build_runner)
Dart
20
star
29

architecture

Dart
20
star
30

di-example

Dart
20
star
31

flutter_calculator

Sample project using project structure generated by Slidy and TDD
Dart
20
star
32

calamidade-backend

TypeScript
19
star
33

FunctionalDart

Código e anotações da série de estudos "Dart Funcional"
Dart
19
star
34

queue_management

Todas as quintas as 17 contamos esse app
Dart
18
star
35

dart_pub

Private pub.dev for packages Flutter Dart
Dart
15
star
36

flutterando_metrics

Dart
15
star
37

flutter_web_site

Flutterando's site built in Flutter Web
Dart
14
star
38

Desafios

Desafios propostos pela equipe do canal
14
star
39

animated-toolbar

Dart
12
star
40

Flutter-SOLID-Hasura-Firestore

Dart
12
star
41

website

Flutterando website
Dart
12
star
42

flutter_mobx_helpers

Widgets and Utils for increment mobx
Dart
8
star
43

carrinho_compras

Aplicativo para modelo de projeto com Repository Pattern e BLoC
C++
8
star
44

auth-service

Go
8
star
45

flutterando_bricks

Dart
8
star
46

shelf_graphql

Graphql server for shelf
Dart
7
star
47

todo_mobx

Dart
7
star
48

full_coverage

A package script for allowing coverage test tool to see all Dart files
Dart
7
star
49

flutterando_analysis

Lint rules for Dart and Flutter used internally at Flutterando
C++
7
star
50

iugu

Dart
6
star
51

via_cep_search

C++
6
star
52

slidy-vscode

snippets for slidy
TypeScript
6
star
53

semana-do-flutter-rx-notifier

Dart
6
star
54

crud_completo

CRUD Completo
C++
6
star
55

dart-backend

Dart
6
star
56

asp_arch

C++
6
star
57

unity_test_study

Dart
6
star
58

fteam_authentication

Dart
5
star
59

builders

Use Consumer, Select and BlocConsumer in any System Injector
Dart
5
star
60

dialog_launcher

A Dart package to facilitate the creation and handling of dialog boxes in command line interfaces across different operating systems (Windows, Linux, and macOS).
Dart
5
star
61

clean_dart_search_getit_cubit

Dart
4
star
62

shelf_hasura_actions

Shelf Middleware for Hasura Action.
Dart
4
star
63

hook_state

`hook_state` is a Flutter package inspired by React hooks and the `flutter_hooks` package. It offers a similar hooks experience but without the need for additional widgets, allowing you to use just `StatefulWidget` to manage complex states declaratively and reactively.
Dart
4
star
64

value_listenable_test

Assists in testing ValueListenable objects (ex: ValueNotifier).
Dart
4
star
65

value_selectable

A Flutter package that provides computed values for `ValueNotifier`, inspired by the Selectors from Recoil.
C++
4
star
66

slidy_extension

TypeScript
3
star
67

hasura_cache_interceptor

Package to implements cache in Hasura package
Dart
3
star
68

passport

Dart
3
star
69

flutter-latam-conf-2020-site

A site for Flutter LATAM Conf 2020
Dart
3
star
70

value_notifier_plus

ValueNotifierPlus é um pacote que expande as funcionalidades de ValueNotifier do Flutter.
C++
2
star
71

hive_cache_interceptor

Package to implements cache using hive in hasura_connect package
Dart
2
star
72

.github

2
star
73

hasura-webhook-auth

Dart
2
star
74

flutterolx

Dart
2
star
75

keyframe

Improve the animation experience in Flutter by transforming Tweens into Timeline with Keyframe like a video editor, but using programming language
C++
2
star
76

hasura_core-Deprecated-

Dart
1
star
77

flutter-latam-conf-2020-challenge

A challenge for Flutter LATAM Conf 2020
1
star
78

hasura_firebase_performance

Hasura connect Interceptor implementation that sends querys metric data to Firebase.
Dart
1
star
79

flutterando_workflows

Reusable GitHub Workflows used at Flutterando
1
star
80

precache_image_builder

Dart
1
star
81

shared_preferences_cache_interceptor

Package to implements cache using shared preferences to hasura_connect package
Dart
1
star
82

matchmaker_lol

Dart
1
star
83

signals

Dart
1
star
84

coopartilhar-api-mock

JavaScript
1
star