Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
/Alfie-AndroidPublic template

This repo is part of Alfie: a project to boostrap eCommerce Projects (Front-end mobile/web and BE and Graph APIs)

License

NotificationsYou must be signed in to change notification settings

Mindera/Alfie-Android

Repository files navigation


This is a repository for an e-commerce Android app template.

Features


work in progress 🚧

Architecture


This application code base is structured by taking into account theofficial architecture guidance. This means following an MVVM approach to layers, modularisation of the different layers to keep the code as independent as possible, and adherence to the Clean design philosophy.For the network layer, a GraphQL service will be responsible for handling all our requests, and we will be using Apollo Kotlin as a client on our end.On the UI side, we are using Compose for every screen and feature.

The application is composed by these main modules:

  • App: main application entry-point
  • Network: core request logic and generic response handling
  • Data: responsible for integrating FPS services
  • Domain: contains the business logic and has no dependencies from other modules
  • DesignSystem: style system and UI components
  • Feature: UI features and screens

Main tools

Development

Naming

  • The interface should have a simple name (BagRepository) and the class implementing it should have theImpl suffix (BagRepositoryImpl)
  • Data Models
    • API response models (DTOs) are generated by Apollo Kotlin using the declared*.graphql script files, contained within thedata layer - in this example, the class would be generated asGetBagQuery.Data
    • The main model should be a simple name (Bag) and be defined in the Domain Layer, exposed from the Data Layer and exposed to the Presentation Layer, to avoid having dependencies on the Domain Layer
    • The UI model should be named withUI suffix (BagUI) and be used only in the Presentation Layer
  • When necessary, Database models should be named withEntity suffix (BagEntity)

Apollo Kotlin and GraphQL queries

GraphQL queries are stored in thedata layer, within thegraphql directory. Each should be within its own subdirectory, to keep everything as organized as possible. The following is an example of how it should look like:

  • graphql
    • account
      • GetUser.graphql
      • UpdateUser.graphql
    • bag
      • GetBag.graphql
      • AddItem.graphql
    • product
      • GetProduct.graphql

UI

Jetpack Compose is the default for every UI feature and screen.

UI Features

Each major app feature should be implemented as a module, so that its screens and ViewModels are completely inaccessible from other feature modules, thus keeping everything properly compartmentalized. An example structure of modules could look like the following:

  • feature
    • account
    • bag
    • checkout
    • home
    • pdp
    • wishlist

Design system

Theme default values and stylistic options are to be defined in thedesignsystem module, including colors, dimensions, shapes, fonts, etc.

Reusable components such as buttons, badges, loaders, among others, should be implemented on this module, so that they can easily be used in multiple screens whenever necessary.

UI standards and organization

Within each feature module, we add everything necessary to connect to thedomain layer and construct the user interface and experience. This is usually consists in creating some files, which follow some software design patterns:

  • UI models: they represent the data to be shown on screen
  • Factories: the preferred way to create UI models, generally using data coming from thedomain layer - avoid the usage of simple mappers, as those become harder to expand upon
  • ViewModels: where the state and logic for each screen is handled
  • UI screens: where Composables are defined - should only have the logic strictly necessary to show the state, as defined by the ViewModel

Navigation (Compose Destinations)

Compose Destinations is the library used to support the navigation in the application.

As of now, Destinations code generation is enabled only for the:feature:* modules (through thealfie.feature plugin) and the screens should be implemented on those modules. If a screen needs to be implemented on a different module, extra setup will be needed to activate the code generation on that module.

Creating a screen that is accessible across the app

  1. Create the composable for the screen on the feature module
  2. (if has arguments) Create a*NavArgs data class on theargument package of the:core:navigation module with the arguments for that screen
  3. Annotate the screen composable with@Destination
  4. (if has arguments) Pass the previously created*NavArgs class on thenavArgsDelegate parameter of the@Destination annotation
  5. Run the code generation. This can be achieved by building the project or runninggradle kspDebugKotlin
  6. Add the generated class to theNavGraphs object (:app module). It is usually added to theroot nav graph, but new nav graphs can be created
  7. Create sub-class for that screen on theScreen sealed class (:core:navigation module)
    1. If has no arguments, it can be adata object
    2. If has arguments, it can be adata class with theargs: *NavArgs as a field
  8. On theDirectionProviderImpl (:app module) add the mapping from the newly created sub-class to the generated Destinations class for that screen

Creating a screen that is accessible only on its feature module

Creating a screen accessible only on its feature module is similar to creating a screen with global access (as described on the previous section) but without doing the steps 7 and 8.

On the step 2, the*NavArgs class can be created on the feature module instead of the:core:navigation module.

Creating a bottom sheet

For the library, the bottom sheets are treated as destinations (like the screens). Creating a bottom sheet is similar to creating a screen with a small adaptation on the step 3: the classDestinationStyleBottomSheet::class should be passed on thestyle parameter of the@Destination annotation.

Dismissing the bottom sheet is as simple as executingpopBackStack() (ornavigateUp()) on theDestinationsNavigator.

Creating a dialog

The dialogs can also be considered destinations by the library. Similarly to the bottom sheets, the styleDestinationStyle.Dialog::class can be used.

For simple dialogs (e.g. confirmation dialogs with actions) it might be easier to implement them in the normal way instead of creating a destination.

Navigating to a destination

  1. Inject theDirectionProvider on the screen composable and use thefromScreen function to get theDirection from theScreen sub-class
  2. Inject theDestinationsNavigator on the screen composable andnavigate to theDirection.

If the destination is on the same module, there is no need to do the step 1, as the Destination class can be used directly on the step 2.

Accessing the destination arguments

When usingnavArgsDelegate, the destination arguments can be obtained on the ViewModel through theSavedStateHandle (which can be injected with Hilt). ThenavArgs() extension can be used on theSavedStateHandle to get the arguments class.

If the destination has no ViewModel, it can be obtained through theNavBackStackEntry (which can be injected on the screen composable). TheargsFrom(navBackStackEntry) function of the destination class can be used to get the arguments.

Branch naming

The name of the branch should start with the ticket name followed by branch specific name[JIRA-ticket]_awesome_feature

Branch organization

Branches should be contained in subdirectories (feature/,bugfix,chorerelease/ orhotfix/) making them more manageable and easier to organize.

  • feature is the subdirectory for every ticket that adds new code to the repository
  • bugfix should be used for every bug fix that is not being immediately deployed into production
  • chore used for simple maintenance tasks which do not require going through QA
  • release is to be used only when creating new app versions
  • hotfix is intended for bug fixes which will be applied to production as soon as possible

Takingthese guidelines as a reference, the commit message should represent the nature of the work as well as the ticket associated with it, so it is easier to later on understand the context in which the change was done.

The template of the commit message is{[JIRA-ticket]} {desc} and as an example of a feature commit associated with ticketXXAA-1 it would be[XXAA-1] Awesome feature boilerplate

Workflow

Peer Review

  • A pull request needs at least 2 approvals before being sent for testing by QAs or merged
  • If there are enough approvals but there are pending comments, those need to be addressed and resolved before testing or merging
  • In case of UI additions or changes, please try to add a screenshot, video or GIF to make it easier to understand
  • Always add a comment explaining the context of the work
  • Squash merge cleaning up message history if needed and follow thecommit message convention as specified above
  • If reviewing, you are responsible for resolving the discussions if you're OK with the reply or changes done
  • If MR owner, reply with "Done" or react with 👍 and avoid resolving the comment since it will resolve the discussion and is harder for the reviewer to pinpoint the changes

Git Workflow

Follow theGitflow Workflow for creating feature branches as well as managing releases and hotfixes or any other type of work that might be needed

Lint

Lint is one of the validation steps for any pull request. For that, the toolDetekt is used.

The tool configuration can be found inconfig/detekt/detekt.yml. That's where all rules are defined and can be configured.

To run the tool, execute the following Gradle task:gradle detekt

When lint fails, follow one of the following approaches:

  • Fix the issue pointed by the tool
  • You can attempt to fix it automatically with the--auto-correct flag - this only works for formatting issues
  • If you think the pointed rule should be changed, ask the team and if everyone agrees edit theconfiguration file
  • If you think you have an exception to the rule, either:
    • add it manually to thebaseline file
    • automatically generate the baseline file by running the following Gradle task:gradle detektProjectBaseline
      • Attention: this will add all the identified issues to the baseline. Make sure to only run this task when the exceptions are the only issues identified.

Besides thedetekt Gradle task, we suggest using theDetekt Android Studio plugin in order to have the lint warnings in the code (the plugin canbe configured with our configuration file to follow the same rule set).

CI/CD


CI


[Workflows & Pipelines]

Workflows are a set of steps that can have the usual required actions to run a pipeline such as cloning the branch, restoring cache or deploy run results.

It also supportsFastlane integration where we can set lane calls and pass arguments we might need. This also allows to offset most of the Android specific tasks to Fastlane and keep CI responsible only for the flows and steps.

Supported Workflows

  • [branch_validation]
    • Detekt: check if linting rules are applied
    • Unit Tests: checks if all modules' tests pass

Triggers

Each trigger type requires values that can be set as a regex to match different branch names we want the triggers to take effect. We also need to assign a workflow we want to be run when these are triggered. Three main types of triggers can be set and used:

  • Push: needs a format for thepush branch and might be useful to run specific tasks before opening a PR, but currently not being used
  • Pull Request: needs a format forsource branch and thetaget branch and it will be ran when opening a Pull Request as well as any commit that is pushed afterwards. This is the main trigger being used throughout the development lifecycle and is also enabled for Draft Pull Requests
  • Tag: needs a format for thetag and will run the associated workflow when a tag is pushed and is currently not being used (review on releases distribution)

YAML Configuration

Besides the dashboard UI oriented configuration available, we can also use the more traditional configuration file. This is pushed to the repository and then CI can pick it up and run the pipeline accordingly. This way we have full control of the versioning and it can also go through the normal peer review process as any other change to the project.

The dashboard visual representation and the YAML configuration file are interchangeable so a change in one will be reflected on the other. This offers the flexibility to use the approach that best works for you, keeping in mind that any change still needs to update the configuration file.

One downside is that the YAML file will have the configuration for all the workflows, which can make the file quite busy, so we should put as much tasks in theFastfile as possible, also making us more futureproof in case the CI/CD tool changes.

CD


The Workflows used for CD will be chained with thebranch_validation workflow and only when it has successfully finished, which means that each workflow will be responsible for its build variant and we can reuse thebranch_validation workflow for shared tasks we need.

Supported Workflows

Development Branch

Every time Pull Request is created that does not match a release branch format, it will kick thedelivery_firebase_debug workflow and distribute it to theQA group

  • [delivery_firebase_debug]
    • Debug Build: check if build is ran successfully for Debug variant
    • Chained workflow: runs thebranch_validation workflow before
    • trigger:pull request where the source and target branch matches any branch name
Master Branch

Every time there is a change pushed intomaster it will kick thedelivery_firebase_debug workflow and distribute it to theQA group

  • [delivery_firebase_debug]
    • Debug Build: check if build is ran successfully for Debug variant
    • Chained workflow: runs thebranch_validation workflow before
    • trigger:pull request where the source and target branch matches any branch name
Release Branch

The release branch will need to have the formatrelease/Alfie-M.m.p and once a Pull Request is created, it will kick thedelivery_firebase_beta workflow and distribute it to theMindera group

  • [delivery_firebase_beta]
    • Beta Build: check if build is ran successfully for Beta variant
    • Chained workflow: runs thebranch_validation workflow before
    • trigger:pull request where the source matchesrelease/Alfie-M.m.p and the target branch can be any name
    • versions: will update the name and code versions only once in case the gradle version does not match the branchM.m.p

The pushed tag will need to have the formatrelease-M.m.p and once pushed, it will kick thedelivery_release workflow and distribute it to theMindera group

Release Tag
  • [delivery_release]
    • Release Build: check if build is ran successfully for Release variant
    • Chained workflow: runs thebranch_validation workflow before
    • trigger:tag that needs to be pushed with the formatrelease-M.m.p

Firebase App Tester Groups

There are different target audiences depending on the type of build we are distributing:

  • QA: Quality Assurance team will receive
  • Mindera: will include theQA group as well as the rest of the internal Mindera product team
  • Alfie: Alfie stakeholders

Build


work in progress 🚧

Testing

We aim to achieve the highest test coverage by area/class responsibility instead of overall project coverage percentage. By doing so, we make sure that we important logic and state handling

  • Data
    • DTO to Domain mapping
    • Mappers/Factories
  • Domain
    • Use Cases
    • Business logic
    • Mappers/Factories
  • Presentation
    • View Model state/events handling

Testing libraries and tools

Code coverage

The code coverage report can be generated by running the taskgradle :app:koverHtmlReportRelease.

Some filters are being applied in order to have the coverage metrics only for the testable files/classes/functions. If new filters are needed, they can be added on theAppConventionPlugin.kt configuration.

About

This repo is part of Alfie: a project to boostrap eCommerce Projects (Front-end mobile/web and BE and Graph APIs)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp