Skip to content

sagar-deriv/flutter_component_architecture_poc

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Table of Contents

  1. What is Software Architecture?
  2. Difference Between Software Architecture and Design Patterns?
  3. Key Goals of Effective Software Architecture
  4. Current Mobile Team Architectures
  5. Proposed Architecture

What is Software Architecture?

Software Architecture is the conceptual foundation and strategic framework for a software system. It encompasses the fundamental design decisions that shape the system's structure, behavior, and interactions. Like the architectural plan for a building, software architecture defines the arrangement of components, the pathways of data, and the mechanisms for achieving desired qualities such as performance, scalability, and maintainability. It serves as a blueprint that guides developers in creating a cohesive, reliable, and adaptable software solution that aligns with the project's objectives and user requirements.

Difference Between Software Architecture and Design Patterns?

Software architecture and design patterns are both crucial concepts in software development, but they serve distinct purposes and operate at different levels of abstraction. Let's delve into the differences between these two concepts:

Software Architecture

Software architecture refers to the high-level structure and organization of a software system. It outlines the major components, their interactions, data flows, and the overall design decisions that shape the system.

Scope: Architecture provides the fundamental framework for the entire software application. It defines the system's major building blocks, their relationships, and the principles that guide their interactions.

Abstraction Level: Architecture operates at a higher level of abstraction compare to design pattern, focusing on the system as a whole. It addresses questions like how components collaborate, how data moves, and how non-functional requirements are met.

Goals: Architecture addresses key system-wide concerns such as scalability, performance, security, and maintainability. It sets the strategic direction and lays the groundwork for the entire project.

Design Patterns

Design patterns are reusable solutions to common software design problems. They provide templates for solving specific design challenges and promoting best practices.

Scope: Design patterns are small-scale solutions that target specific design problems within components or interactions. They help improve the design quality of individual parts of the system.

Abstraction Level: Design patterns operate at a lower level of abstraction than architecture. They focus on recurring design problems and offer guidelines for structuring code within components.

Goals: Design patterns contribute to modular code, enhanced reusability, and improved code maintainability. They aim to simplify complex design decisions, foster code consistency, and encourage collaboration among developers working on different parts of the system.

Key Goals of Effective Software Architecture

Crafting a successful software architecture involves aiming for specific objectives that collectively contribute to the creation of a reliable, adaptable, and user-centric system. The following are the core goals that guide the development of effective software architecture:

Scalability: The architecture should support handling increased demands by adding resources or components and maintaining performance and responsiveness.

Maintainability: The architecture should make it easy to identify, fix issues or enhancements, minimizing the cost and effort of ongoing maintenance.

Modularity: Components should be organized into discrete, cohesive modules that can be developed, tested, and maintained independently, promoting reusability and ease of updates.

Reusability: Design decisions should facilitate the reuse of components or patterns across different parts of the system or in future projects.

Testability: Designing the architecture to facilitate comprehensive testing, enabling efficient validation of the system's functionality, performance, and reliability.

Current Mobile Team Architectures

Deriv P2P Architecture

Deriv P2P Architecture Overview

Architecture Components

BinaryApiWrapper: This component is responsible for connecting to the Flutter Deriv API. In fact We need this component to make Flutter Deriv API compatible with P2P app.

BaseAPIRepository: This is a base class that provides common methods and functionalities for repositories that interact with APIs.

Features Repositories: These repositories utilize the BinaryApiWrapper to interact with the Deriv API and provide data to the feature cubits.

Feature Cubits: Cubits are a part of the Bloc pattern architecture, commonly used with Flutter apps. They manage the state of the UI related to specific features. Each feature cubit extends the BaseCubit class, which includes states like InitialState, LoadingState, LoadedState, and ErrorState.

Challenges

Dependency Injection and Component Replacement: The architecture lacks proper dependency injection, making it challenging to replace components without affecting the entire system. This can lead to difficulties in swapping out components, such as changing the data source or replacing UI elements, potentially leading to tight coupling and inflexibility.

Testing Complexity: The intricate dependency flow and the tight coupling between cubits, repositories, and UI elements can complicate testing. Testing becomes more complex due to the interwoven nature of components, potentially resulting in less effective unit testing and a higher likelihood of integration issues.

Limited Base State and Flexibility: The architecture's Base State is limited to a specific set of states, which might not cover all possible scenarios. This limitation can restrict the flexibility to handle a wider range of UI states and can lead to less adaptable and more rigid user interfaces.

Lack of DataSource Layer: The architecture doesn't seem to incorporate a dedicated DataSource layer for handling data retrieval and API communication. This omission can result in difficulties when trying to change the way the application connects to APIs or external data sources, hindering the ability to adapt to changing requirements or technology advancements.

Deriv GO Architecture

Deriv Go Architecture Overview

Architecture Components

Flutter Deriv API (Data Source): This component serves as the Data Source for the application, providing access to the Flutter Deriv API. It abstracts the API communication details and data retrieval logic.

Base Feature Service: Each service has its own Base Feature Service that provides common methods and functionalities for that specific feature.

Feature Services: These services are responsible for interacting with the Flutter Deriv API. Each service corresponds to a specific feature or domain within the app.

Feature Cubits: Cubits are used to manage the state of the UI related to specific features. Each cubit is associated with a specific feature and contains the logic for managing UI states.

Bloc Manager: This component employs the Observer Pattern to manage dependencies between different feature cubits. It emits states to dependent cubits based on state changes.

Challenges

Direct Data Source Connection by Services: Connecting to the Data Source (API) is managed directly by the services, potentially leading to tight coupling between the services and the API. This can make the code less flexible and harder to adapt to changes in the API or data source. It might also lead to difficulties in swapping out the data source if needed.

Complexity of State Dependency Management by Bloc Manager: The complexity of managing state dependencies using the Bloc Manager could lead to intricate relationships between different feature cubits. This complexity might make the application harder to understand, debug, and maintain. It could also lead to challenges in writing effective unit tests for the interactions between cubits.

Unclear Component Responsibilities: The architecture might not provide clear and well-defined responsibilities for each component, making it challenging to determine where specific logic should reside. This lack of clarity can lead to confusion among developers, code that's difficult to maintain, and potential overlaps or gaps in functionality.

Proposed Architecture

In the dynamic landscape of modern software development, a robust and adaptable architecture is critical to the success of any project. The proposed software architecture, inspired by the principles of Clean Architecture, is aligned with the unique demands of our projects. By seamlessly integrating the core tenets of Clean Architecture with carefully tailored adjustments, we aim to empower our development teams to create applications that are testable, reusable, modular, and scalable and enable developers to collaborate seamlessly.

At the heart of the proposed architecture lies a dedication to fundamental design principles that foster maintainability, flexibility, and growth. We aim to improve how our applications are developed and maintained. The architecture rests on five pivotal pillars that shape its core essence:

Composition over Inheritance

In a world where software systems are growing increasingly complex, the principle of Composition over Inheritance gains even greater significance. This approach advocates building functionalities by composing smaller, more manageable components rather than relying heavily on complex inheritance hierarchies. By embracing composition, our architecture promotes flexibility, maintainability, and easier adaptability to changing business needs.

Program to Interfaces

The principle of Program to Interfaces encourages the use of interfaces to define contracts for components. This approach promotes loose coupling and reduces dependencies, making it easier to swap out components without affecting the entire system. By following this principle, our architecture fosters flexibility and adaptability, enabling developers to make changes without causing ripple effects throughout the codebase.

Single Responsibility (SR)

The principle of Single Responsibility underscores the importance of assigning a singular purpose to each module or component. By adhering to this principle, our architecture fosters clarity in design, isolates potential points of failure, and facilitates ease of maintenance. Modules dedicated to a specific task reduce interdependencies, enhancing the overall stability of the system.

Separation of Concerns (SoC)

Clear boundaries between different aspects of the software are achieved through the principle of Separation of Concerns. By modularizing functionalities based on their distinct responsibilities, our architecture promotes comprehensibility, ease of debugging, and promotes a streamlined development process. This separation also simplifies the integration of new features or changes without causing ripple effects across the entire codebase.

Open-Closed Principle (OCP)

The Open-Closed Principle advocates that software entities should be open for extension but closed for modification. By adhering to this principle, our architecture ensures that new functionalities can be added without altering existing code, thereby reducing the risk of introducing unintended side effects. This promotes a smoother development workflow, reducing regression risks and enhancing code stability.

Dependency Inversion (DI)

The Dependency Inversion encourages the decoupling of high-level modules from low-level implementation details, promoting a more adaptable and interchangeable architecture. By reversing traditional dependency flows, our architecture mitigates the risks associated with tight coupling, making it easier to substitute components and manage changes without cascading impacts throughout the system.

Architecture Components

Core

Core Architecture Overview

Core part is primarily focused on defining contracts and interfaces that features must implement. This is a form of contract-driven design, where you're establishing a set of rules and guidelines that each feature should follow. This approach can provide clear boundaries and consistency across application architecture.

Base Data Source

This class serves as a contract that defines the methods and structure that feature-specific data sources must implement. It sets the guidelines for how data should be accessed and manipulated.

Base Model

Base Model is more about setting a guideline for what properties each feature-specific model should have. It can also be used to define common properties that should exist in each feature-specific model.

Base Entity Class

Similarly, the base entity class can provide guidelines for common fields that should exist in each feature-specific entity.

Base Mapper

The base mapper class defines the contract for mapping data between different layers. Each feature would implement its own mapper that adheres to this common contract, ensuring a consistent approach to data transformation.

Base Repository

The base repository class establishes the contract for data manipulation methods that should be present in feature-specific repositories. This ensures consistency in how data is handled.

Base Service

The base service classes define the contract for services that features can use. Depending on whether a feature needs to interact with the data layer through the repository or not, the appropriate base service can be chosen.

For example, if a feature needs to interact with the data layer through the repository, then it can use the BaseServiceWithRepository. If a feature doesn't need to interact with the data layer through the repository, then it can use the BaseService class.

Feature

Feature Architecture Overview

The feature architecture follows a modular and structured approach, facilitating the development of individual components. By dividing each feature into distinct layers, including Data, Domain, Interactor, and Presentation, we establish clear boundaries and responsibilities.

The Data layer encompasses data models, mappers, repositories, and data sources, ensuring consistent data access and manipulation.

The Domain layer focuses on core business entities and feature-specific repository contracts.

The Interactor layer hosts feature services, housing business logic and communication with repositories.

Finally, the Presentation layer handles UI state management and interaction, enhancing user experience through effective widget composition.

Data Layer

Data Layer Architecture Overview

Feature Model: This represents the data structure specific to the feature. It only contains the properties that are relevant to the feature.

Feature Mapper: This is responsible for mapping data between the feature model and other layers of architecture.

Implementation of Base Feature Repository: This is where you implement the methods defined by the base feature repository contract in the domain layer. It provides actual data manipulation functionality for the feature.

Feature DataSource Abstraction and Implementations: The feature-specific data source classes provide the means to access data for the feature. The abstraction ensures a consistent contract for data access methods, and the implementations interact with actual data sources like APIs, databases, etc.

Domain Layer

Domain Layer Architecture Overview

Entity: The entities within the domain layer represent core business objects that the feature deals with. These might mirror the structure of data retrieved from the data source.

Feature Repository Interface: This defines the contract for interacting with the feature's data. It specifies the methods that should be available for data manipulation and retrieval.

Interactor Layer

Interactor Layer Architecture Overview

Base Feature Service: This abstract class defines the structure and methods for feature-specific services. It might include business logic, data processing, and interaction with repositories.

Service Implementation: Each feature implements its own service class that extends the base feature service. This is where you implement the specific logic and operations related to the feature.

Presentation Layer

Presentation Layer Architecture Overview

State Management: This is where you implement the state management solution for the feature.

Extended State Management: This class extends the state management class and acts as a bridge for interactions between different feature state management instances. It helps maintain a centralized point for communication between different UI components within the feature and also keeps the State Management Solution class clean.

Page and Widgets: This is where you implement the UI for the feature. It includes the page and widgets that make up the feature's UI.

Cross Feature Communication

Cross Feature Communication Architecture Overview

  • Using Services from Another Feature

    If you have a service defined in one feature (Feature A) that you want to utilize in another feature (Feature B), you can achieve this by injecting the service into state management class of Feature B. This means that the state management class acts as a central hub for providing access to various services across different features.

  • Sharing State Updates

    When you need to access or receive updates about the state of a feature from within a different feature, you should inject the necessary state into the extended state management class. This extended state management class would likely handle state updates for multiple features, allowing them to communicate with each other by sharing state information. States could be shared between features using the following approaches:

    • Using a simple stream of states or an intermediate model.
    • Using a design pattern like Observer Pattern or Mediator Pattern.
    • Any other approach that makes sense for the specific use case.

Example

Example of how to use this architecture can be found in the example folder.

Example Overview

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dart 60.8%
  • C++ 18.0%
  • CMake 14.8%
  • Ruby 2.2%
  • Swift 1.5%
  • HTML 1.5%
  • Other 1.2%