Skip to content

Onboarding for Existing Journeys

How to use this document

If it is unclear where to begin, start with section Journey Decision Tree. This will give you a rough heading and help initiate the conversation with business, product and technology. The introduction contains some of the whys and wherefores regarding our reasoning and thought process - it may help frame the order of the problem we are all addressing. :::

Introduction to PlatformHub

The Mobile X platform and app were created to bring mobile apps to the market cheaper and quicker through reuse, separating features into journeys with a common build and test pipeline and process. This has been a great success with apps in 24 stores for each platform iOS and Google Android. However as the platform has scaled it has because increasingly brittle where poor trade-offs were made between reuse and coupling, with domain knowledge embedded in conditional build directives, runtime configuration branching and blurred ownerships.

In many cases domain state is distributed all over the app and platform, this has made it both statically and dynamically coupled leading to ironically poor reuse (it has become monolith) and slow to respond to change.

There have been a number of contributing factors to this including:

  • Compounded technical debt due to overriding feature focus
  • Poor planning and therefore unrealistic delivery targets leading to further corner cutting
  • Large global teams with poor lines of communication Conway
  • Restructuring and re-organisations diluting team knowledge and subject expertise
  • Lack of staff to lead to teams and voices of technical concern not being heard from the code-face
  • Lack strategic insight to the evolution of the underlying technologies
  • Poor overall system design or no longer fit-for-purpose

What problems does PlatformHub solve

The ability to respond to change is now a key business objective and directive.

  • Rapidly create journeys to add new functionality and features
  • Evolve existing journeys with confidence of no inadvertent side-effects
  • Simplify and cost-effective onboarding to the platform
  • Provide support for release-on-demand - respond to change
  • Build Once Scale Fast / Last Mile (flexible)

Why have we made the choices we have?

Technology constantly evolves. While most would agree with this and righty claim it's obvious we do not always take action to address this pressure. Frequently, the organisation is geared to a feature release policy with no factoring in for maintenance, or a better term responding to change. The industry app evolution system is driven by consumer expectation, with the ecosystem of new devices and operating systems iterating each year. If a customer gets a new feature in one or more apps they will expect in all their apps - almost by muscle memory in some cases. This constant iteration includes our own fintech market where competitors will be adding new features and products to improve the user experience.

Why are we asking you to re-examine, refactor and restructure journeys that have undergone a number of iterations previously? Essentially we have to start addressing the ongoing requirement and pressures to evolve the platform, previous versions of the platform were responding to. We are now five years / iterations behind OS upgrades and numerous hardware iterations (including Apple extension the ARM specification to their Mac line ups) across the Apple and Google Android landscape.

We now need to think strategically and increase our ability to respond to change while reducing overall system entropy (tech debt, poor design decisions, measuring etc). To do this we looked at our existing system design broadly through the lens of Complexity Theory (specifically The Cynefin Framework), Systems Thinking and more pragmatically listening to our engineers and leaders. Our code base is a complex emerging system that cannot guarantee behaviour i.e. changing one section of code could side-effect another either at build time, runtime or a combination of both. It's important to note here the emerging code is not necessary evolving.

To improve and evolve our system we need control and increases in entropy, we need to provide safeguards against changes in the underlying OS platform and ensure consistence behaviour while supporting global teams.

Journey Decision Tree

The decision tree shows the steps that need to be followed when assessing a journey to onboard to PlatformHub. Follow the flow down, information about each step can be found below the diagram.

Diagram showing decisions when onboarding a journey

Detailed Steps

These steps should be read in conjunction with the diagram above.

Entry Points

ActivityDescription
Existing Native JourneysThese Journeys which are implemented in java/kotlin/swift/Obj-C and use the existing platform capabilities including MDL. Should be stable and solve specific problems however their dependencies and side-effects could be throughout the platform. Code quality may vary depending on release pressures, age of component and team expertise.
New Native JourneysNew features and functionalities which are planned and budgeted for.
Existing Hybrid JourneysThese are journeys that are implemented in native code and are also using the WebComponent (WebView wrapper). The web pages displayed may either be app specific (possibly even local) or reused from the browser journey.
Existing non-mobile appThese are either web pages or a specific web app journey where the value stream wishes to save money and get onto the mobile app quickly.

Analysis

ActivityDescription
Determine Business Value
  • Is the feature visible to the Customer?
  • Is it a unique selling point?
  • What are the associated KPI/KVIs? (what is good?)
  • What is it's roadmap?
  • How global is the journey and is customised per market? (beyond localisation)
  • Does it bring financial value?
  • Does it sell other products?
There are more questions which will be defined by the Product Owner essentially the goal is to identify how important the journey is to the business and customer. The outcome should be a high value journey would benefit from a complete PlatformHub integration (vs adapter).
Determine Coupling and ComplexityThe Beacon Measurement product which is currently in progress captures the source code metrics and github activities. These metrics are detailed and follow recognised industry standards. They will give the deep insight into how maintainable the code is and how well it is designed. Complex and highly coupled code is an indicator the code will become increasingly difficult to maintain and will eventually require a rewrite.
Measuring will also become a yardstick for our progress in simplifying the codebase as we migrate to PlatformHub.
Decision FactorsThere are a number of other decisions built on top of business value + complexity such as; expected life span, feature sophistication roadmap, data reuse and scaling to new markets. All the factors will need to be taken into account by a select committee consisting of business, product and tech.

High Complexity CouplingLow Complexity Coupling
High ValueRewrite to full PlatformHub PluginRefactor existing code and build as Full PlatformHub Plugin
Low ValueCreate adapter and plan to move to PlatformHub fullyRewrite as a simple composite plugin

Design

Activity

Event Storming and Domain-Driven Design

Description

The primary purpose of both of these complimentary techniques is to agree a common description of the journey's function, which will include:

  • An agreed language between the business and technology
  • Ownership and Responsibilities
  • Commands, Actions / Intentions (UI/UX)
  • Domain Models
  • Domain Events CQRS(Command & Query Responsibility Segregation) Transactions : Write and Read Responsibilities
  • Separation of common models (i.e. Reuse)

Much of the initial work will be captured by EventStorming and should centre around meaningful conversations between business and technology reducing the intermediary layers as much as possible. Following these discussion more formal domain designs should be created:

  • Domains Defined
  • Bounded Contexts
  • Ubiquitous Language
  • Finer grained items
    • Values Objects (no id - attributes : name , address, push permissions etc)
    • Entities (ids - such as customers, accounts no etc)
    • Domain Events
    • Aggregates (combining models - reuse)
  • Screen mock ups

The Domain modelling should be driven primarily by architecture with ongoing interactions with business, UI/UX design, product and engineering, the process is highly iterative. At the end of which there should be:

  • Clear UI and UX commands and interaction results e.g. how the screens should interact with our customers
  • What the domain data models should look like and how their states evolve over time
  • Dependencies on other models
  • API required including alignment with their Domain Models and any transforms required
  • Capabilities Required
  • Rough technical architecture

While EventStorming and DDD are used for new journeys, they may also be used on existing journeys to confirm requirements, add any additional features and provide a framework for refactoring. If journeys have followed good design principles to begin with such as OOD, the transition will be seamless with positive results.

Plugin Types

ActivityDescription
(Create) Adapter PluginFor journeys that have simple state and / or simple API's but contain a degree of complexity either internally in code or within the UI/UX, it may make sense to quickly wrap in an adapter to onboard the PlatformHub. At a later point refactoring to a native PlatformHub plugin can take place at a more measured pace
(Create) Simple Composite PluginA native PlatformHub plugin that contains very little state or data. For example displaying text screens or data which it has retrieved via the Mobile Content Store and needs very little domain data business rules (state management). Consequently there is no need for a separate Domain Model Plugin. The plugin may still publish events and the number of events that are consumed should be a rain check on whether the composite will eventually need to be further decomposed.
(Create) Domain Model PluginThe Domain Model is the model of the data (state) and how that data changes over time. Actions transition from one state to another. The structure of the model should come out the Domain-Driven Design element of the PlatformHub design process however Object Oriented Design will also provide the same capabilities.
A Domain Model Plugin should be state isolated i.e. the single source of truth for the state of the model, it should follow the Actor Model which will support asynchronous/concurrent operation allowing for performance by design e.g. background loading content.
Domain Models are also units of reuse publishing events (which are further encapsulated as reactive values) which maybe consumed by any Journeys subscribed to the events/values.
(Create) Reactive View PluginThe UI and UX separated into:
  • The view layout itself : Declarative UI
  • ViewModel: The Store
  • Actions on the UI : Reducer
  • Data Values: Side-effects / reactive values
  • Global Theme: Design Tokens
The Generative UI project has already delivered a generative approach to UI source creation, so please use the Generative UI Figma plugin to generate the declarative UI and use the code in your project directly, reducing the dependency on MDL.
Engineers are encouraged to use the reactive approach and pass UI state specific events to reducers to update the views, and reactive values for data updates (via side-effects).

UI + UX

ActivityDescription
Design Process (UI+UX)This is the whole process of creating the UI in Figma, and documenting the behaviours. This will increasingly become the design team's responsibility for the creation of the full UI with correct sizing and layout of all views. Currently engineering fill in a number of blanks. Ultimately there will be tools to assist in the capturing intentions such as command ids and navigation intents.
Export and GenerateMobile Platform Foundation: Declarative State Managed UI Framework. Currently work in progress

Implementation

ActivityDescription
Create REDUX UI ModelCombination of the EventStorming capturing the UI and UX behaviours, creating the actual Layout and themes in the UI Design Process and finally creating the Reactive View Plugin including associated Reducers and values (side-effects). The implementation reducers tends to be a simple structure switch:case:enum where the commands are repressed by enums and the required actions are executed in the case statements. Behavioural tests will also be needed to be implemented.
Implement Domain Models and LogicThis follows the domain-driven design and plugin decision above (composite or separate domain data). The models themselves maybe implemented in the best way possible by the developer. However it is in everyone's interest to invest time in maintainability and provide scope for modifying state, adding transactions or allowing for the reuse of the component by careful use-case publishing.
Define Messages and EventsArchitecture Discussion of events here: PlatformHub Architecture The customer actions will have been captured by the design process, these will translate directly as command events while the transactions i.e. data states over time will be refined through domain-driven design. Any data that needs to be communicated via events will be contained within event payloads (Event-Carried State Transfer).

Release Process

ActivityDescription
Platform Review and AccreditationThe plugin will need to be reviewed by the platform team, following the plugin accreditation process. A token (UUID) will be provided to be added to the journey code. All new versions of the plugin will need to be reviewed.
Release to PlatformAs per the standard engineering build process.

Restructuring and Refactoring

This should not be a scorched earth policy where all components and journeys are to be razed and rewritten. In fact, well written code can be rapidly refactored to PlatformHub Reactive Architecture

Prep and Advice

  1. Identify new or re-examine any existing technical debt and code smells using the metric and measurement suite
  2. Plan, focusing on short iterations
  3. Understand Design Patterns and common paradigms
  4. It may make sense to refactor before transitioning (extracting functions or renaming) also restructuring modules (doesn't not always means smaller modules either i.e. regrouping data coupled code - cohesion)
  5. Check unit tests are valid and update any out of date tests
  6. Use tools both existing and explore any tools you may wish to implement yourself

Learning

  1. Understand the Reactive Paradigm: Familiarize yourself with the principles and concepts of reactive programming. Reactive systems are based on the idea of reacting to changes and events rather than relying on explicit control flow.
  2. Familiarise yourself with the updated Architecture Principles.
  3. Refactoring. Recommended reading: Refactoring: Improving the Design of Existing Code

Steps to Migration: Create a Workflow Plan

Diagram showing migration workflow

STAGE ONE : Understand the Structure and Responsibilities

DirectivesPurpose and Next Steps
Understand the existing technical architecture.
  • Components (structural)
  • Dependencies (coupling)
  • Data flow (coupling)
  • Data mutation over time (state)
  • Identify pain points: Areas of high rates of change (Pull Request heavy), complex code and high coupling
Understanding the structure and static relationship of the existing architecture provides a clear map to negotiate into reducing and decoupling the dependencies. Identifying the edges where direct method calls are being made and what data and properties (state) are being transferred surface potential events and reactive values.
Separate the Data, API calls and View Logic.
  • Sketch out the feature at the architecture level. Logically group the data components and view components
Identify the Primary Patterns and Responsibilities
  • View Logic and Data Logic (MVC, MVVM, MVI, VIPER etc)
  • Housekeeping logic (names such as helper, manager etc)

STAGE TWO : Capture and Understand State Changes

DirectivesPurpose and Next Steps
Identify significant occurrences and state changes.
  • Identify the data flow
    • The source of the event (i.e. consumption : what is affecting the change)
    • What the outcome will directly affect (i.e. Producer )
  • How many paths are spawned as a result (essentially conditional branches)
  • This is the initial sketch to understand with Stage Three implementing the producers and consumers taking into account good asynchronous factors
Work towards unidirectional flow i.e. the direction of change to and from.
At a higher level look at the domain models and view models and determine what initiates state change and what reacts to state change.
Implement event producers: Refactor existing components to become event producers. Modify these components to publish events when significant changes occur. Ensure that events are published with the necessary data payload.
Implement event consumers: Identify the components that need to react to events and refactor them to become event consumers. These components should subscribe to relevant events and handle them appropriately. Modify the consumer components to respond to events and update their state accordingly.
Optimistically look for areas which immediately benefit from conceptually:
  • High concurrency
  • Reactive or Event-driven behaviour
  • Complex asynchronous operations
And pragmatically
  • API calls (duplicating similar calls by other journeys or components)
  • Duplicated functionality
  • Complex view updating code
Replace blocking operations with non-blocking alternatives, such as asynchronous APIs or reactive primitives.
Understand the actor model.
Embrace Asynchrony: Emphasise asynchronous programming techniques to improve responsiveness and scalability. Utilize features like futures, promises, or reactive primitives to handle asynchronous operations. Ensure that long-running or blocking tasks are executed on separate threads or event loops.
Identify cohesive areas of functionality with well-defined responsibilities
  • The well driven code with clear pre and post conditions
  • No or low documented side-effects (typically I/O)
  • Well named and low change rate (PRs)
Identify the bounded contexts: Identify the boundaries of the different contexts within your system. Bounded contexts represent cohesive areas of functionality with well-defined responsibilities. Each bounded context may have its own event-driven architecture.

STAGE THREE : Finally put together the understanding

DirectivesPurpose and Next Steps
Identify the data flows within the current architecture (both locally and globally)
  • Separate the code that changes the data from the code that propagates that change
  • Identify properties (e.g. enums for state management, colours for views etc)from business data (e.g. customer account number)
  • Setters and Getters (clarifying generated and hidden helper functionality)
Determine how to transform them into event-driven flows. Events should carry the necessary data to communicate state changes and trigger actions across components.
Reactive Streams: Identify the data flows within your system and model them as reactive streams. A stream represents a sequence of events or data over time. Determine the sources, transformations, and sinks (consumers) of your streams.

Gradual migration: Depending on the size and complexity of the journey, consider a gradual migration approach. Start by refactoring a small portion of the journey using the steps above and gradually expanding the scope. This helps manage risk and ensures a smooth transition. It may makes sense to initially wrap the journey in an adapter to better understand the dynamics and behaviour of the components.

Test and validate: Test the refactored components and their interactions thoroughly. Plugins may be tested independently within test harnesses. Ensure that events are published correctly, consumers receive events, and the system behaves as expected. Validate the performance of the journey architecture under different scenarios. Create comprehensive tests to validate the behavior and correctness of your reactive system. Cover scenarios related to concurrency, event ordering, error handling, and system boundaries. Leverage tools and frameworks designed for testing reactive applications.

Handle Errors and Resilience: Incorporate error handling and resilience mechanisms into your reactive system. Use error handling operators provided by the reactive framework to propagate, recover, or handle errors within your streams. Implement retry, circuit-breaking, or fallback strategies to enhance system resilience

Monitor and Optimise: Once you have migrated to a reactive system, continuously monitor its performance, scalability, and resource utilization. Fine-tune the system by optimising reactive primitives, tuning thread pools, or adjusting backpressure strategies as needed.