image

Bootcamps ilimitados e +650 cursos pra sempre

60
%OFF
Article image
Daniel Passos
Daniel Passos27/09/2025 21:12
Compartilhe

S.O.L.I.D no FLutter

    S - Princípio da Responsabilidade Única (Single Responsibility Principle - SRP): Este princípio afirma que uma classe deve ter apenas um motivo para mudar. Caso tenha mais de uma responsabilidade, deve ser dividida em duas ou mais classes.

    O - Princípio do Aberto e Fechado (Open/Closed Principle - OCP): Este princípio diz que uma classe deve estar aberta a extensões, mas fechada para alterações.

    L - Princípio da Substituição de Liskov (Liskov Substitution Principle - LSP): Este princípio define que uma classe que herda de outra deve ser capaz de substituí-la sem afetar o funcionamento do programa.

    I - Princípio da Segregação de Interface (Interface Segregation Principle - ISP): Este princípio diz que uma classe não deve ser forçada a implementar interfaces que não utiliza.

    D - Princípio da Inversão de Dependência (Dependency Inversion Principle - DIP): Este princípio diz que classes de alto nível não devem depender de classes de baixo nível. Ambas devem depender de abstrações.

    A arquitetura que eu escolhi para exemplificar é a Clean Architecture, geralmente utilizada com o gerenciador de estados BLoC/Cubit

    lib/
    ├── features/
    │ └── products/
    │ ├── data/
    │ │ ├── datasources/ (Fontes de dados: API, local)
    │ │ ├── models/ (Modelos de dados para serialização)
    │ │ └── repositories/ (Implementação do repositório)
    │ ├── domain/
    │ │ ├── entities/ (Objetos de negócio puros)
    │ │ ├── repositories/ (Contratos/Interfaces dos repositórios)
    │ │ └── usecases/ (Casos de uso/Regras de negócio)
    └── presentation/
    │ ├── bloc/ (ou cubit, provider, etc.)
    │ └── pages/ (Widgets da UI)
    └── core/
    └── ... (utils, error handling)

    Para exemplificar o primeiro princípio (S), implementei uma classe ProductRepositoryImpl que implementa a interface ProductRepository. A única função dessa classe é retornar os dados do banco. Além disso, o código também obedece ao princípio da segregação de interface (I), pois a classe implementa somente a interface ProductRepository, que é específica para a sua necessidade.

    Expondo um panorama geral da arquitetura, a camada Domain tem a única responsabilidade de conter as entidades e as regras de negócio (casos de uso). Já a camada Data tem a responsabilidade de implementar os contratos (interfaces) definidos pelo Domain, buscando os dados de fontes externas. A camada de Apresentação (UI) tem a única responsabilidade de apresentar as informações ao usuário. Ou seja, a Clean Architecture já força a utilização do primeiro princípio do SOLID.

    // data/repositories/product_repository_impl.dart
    class ProductRepositoryImpl implements IProductRepository {
    final ProductRemoteDataSource remoteDataSource;
    ProductRepositoryImpl(this.remoteDataSource);
    @override Future<List<ProductEntity>> getProducts() async {
    // Apenas busca e converte os dados, mais nada.
    return await remoteDataSource.fetchProducts();
    }
    }

    Para o segundo principio (S) escrevi a classe abstrata a seguir:

    // data/datasources/product_datasource.dart
    abstract class IProductDataSource {
      Future<List<ProductModel>> fetchProducts();
    }

    E implementei nas seguintes classes concretas:

    // data/datasources/product_api_datasource.dart
    class ProductApiDataSource implements IProductDataSource { ... }
    // data/datasources/product_local_datasource.dart
    class ProductLocalDataSource implements IProductDataSource { ... }
    // NOVA FONTE DE DADOS (extensão)
    class ProductFirebaseDataSource implements IProductDataSource { ... }

    Com uma implementação de classe que depende de uma abstração, não importa a fonte de dados (seja Firebase, SQLite ou uma API), pois sua única função é implementar a funcionalidade getProducts().

    Além de cumprir com o princípio do Aberto/Fechado (O), esse exemplo também obedece ao princípio da Inversão de Dependência (D).

    // data/repositories/product_repository_impl.dart
    class ProductRepositoryImpl implements IProductRepository {
     final IProductDataSource dataSource; // Depende da abstração
     ProductRepositoryImpl(this.dataSource);
     @override
     Future<List<ProductEntity>> getProducts() async {
      // Apenas chama o método da abstração, sem se preocupar com a implementação.
      final productModels = await dataSource.fetchProducts();
      return productModels.map((model) => model.toEntity()).toList();
     }
    }

    Portanto, para concluir, creio que esses exemplos já demonstram o poder do S.O.L.I.D. Ao depender de abstrações, a manutenção futura será muito mais facilitada. Desse modo, mantemos nosso código escalável, ainda que tenhamos que mudar nossa fonte de dados.

    Ademais, a Inversão de Dependência facilitará a implementação de testes futuros, pois nos permite injetar dependências falsas (mocks) diretamente na instância da classe, tornando nossa aplicação mais confiável. Outro fator importante é o Princípio da Responsabilidade Única, que também facilita a manutenção do app e, consequentemente, a implementação de testes unitários.

    Compartilhe
    Recomendados para você
    Luizalabs - Back-end com Python
    PcD Tech Bradesco - Java & QA Developer
    Nexa - Fundamentos de IA Generativa com Bedrock
    Comentários (2)
    Daniel Passos
    Daniel Passos - 29/09/2025 20:23

    De fato, não é a compreensão do princípio, mas sim a definição subjetiva do que pode ser uma única responsabilidade diante de novas regras impostas.

    DIO Community
    DIO Community - 29/09/2025 13:53

    Excelente, Daniel! Que artigo incrível e super completo sobre S.O.L.I.D no Flutter! É fascinante ver como você aborda a aplicação desses princípios em uma arquitetura de microsserviços. Sua análise da Clean Architecture e do uso de BLoC/Cubit como um gerenciador de estados que já força o uso do Princípio da Responsabilidade Única (SRP) é um insight valioso para a comunidade Flutter.

    Você demonstrou, com exemplos de código em Dart, que a arquitetura que você escolheu (Clean Architecture) não apenas cumpre com o SRP, mas também obedece ao Princípio do Aberto/Fechado (OCP) e ao Princípio da Inversão de Dependência (DIP). Sua análise de que o DIP facilita a implementação de testes futuros, pois permite injetar dependências falsas (mocks), torna o seu código mais confiável e demonstra uma visão de engenharia de software madura e profissional.

    Qual você diria que é o maior desafio para um desenvolvedor ao trabalhar com o Princípio da Responsabilidade Única (SRP), em termos de evitar que uma classe assuma muitas responsabilidades e se torne um "Deus Objeto", em um projeto que cresce rapidamente?