4008063323.net

Essential Insights Gained from Architecting an Enterprise Application

Written on

Chapter 1: The Dynamic Nature of Frontend Architecture

The architecture of a frontend application is an ongoing process; it's never a case of "set it and forget it." No design or plan is flawless or entirely comprehensive.

A recent project involved a significant ERP system utilizing DotNet Core microservices for the backend. Our goal was to replicate the functionalities of a legacy system's Windows Forms using Angular, particularly incorporating an editable Angular grid that supports keyboard navigation.

After engaging in discussions with a new business associate, I began crafting an inline editable grid that meets web accessibility standards (A11y). This grid allows users to perform various actions, including:

  • Editing, deleting, adding, and saving rows.
  • Navigating through rows, columns, and cells.
  • Switching a row between editable and non-editable modes using keyboard shortcuts.
  • Implementing autocomplete features in specific columns for efficient value searching.
  • Opening detailed dialogs with F2 key interactions, allowing further navigation and selection.
  • Closing dialogs and returning focus to the grid via keyboard shortcuts.

With this core feature in place, the project team expanded, and we began shaping the application into a comprehensive ERP system. I was appointed as the frontend architect, tasked with developing multiple dynamic components across various modules while refining the code design to simplify integration for new team members.

This article outlines ten crucial lessons I've learned throughout this journey. These insights aren't meant to instruct you on how to design an enterprise web app but rather to familiarize you with common challenges in frontend architecture and software engineering. I'll break down important considerations and offer tips that can enhance productivity and improve web application development.

These lessons stem from my personal experiences and over a decade of learning within the IT field.

1. Embracing the Container-Presenter Pattern

As your project expands, seize opportunities to maximize code reuse among features and modules. This requires avoiding the mixing of concerns, which can complicate code comprehension and maintenance.

To implement this in modern component-based frontend architecture, we categorize component responsibilities using the Container-Presenter pattern:

  • Container Components: These intelligent components retrieve data from services and manage communication with their child components (presentational components) via inputs and outputs.
  • Presentational Components: These solely render the UI and handle user interactions. It's essential to note that presentational components don't need to be entirely passive; they can still possess some logic without having services injected.

In our ERP system, each module features a primary presenter acting as a bridge between container and leaf components, facilitating numerous inputs/outputs to child components without embedding logic. As the complexity grew, the influx of inputs and outputs became challenging to track, resulting in what are referred to as bubbling events.

By injecting services further down the component tree and converting some presentational components into smart components, we alleviated the bubbling events issue, enhancing maintainability.

2. Implementing Parent-Child Design

When multiple components share a similar code structure, you'll often encounter repetitive inputs, outputs, and methods. To eliminate this boilerplate code, I capitalized on component inheritance to centralize shared logic within parent classes. This significantly reduced code duplication.

Employing a parent-child design can minimize human error linked to repetitive tasks. However, it's advisable to adopt generic names, as discussed in the next section.

3. Utilizing Generic Naming

Using generic variable names like dataList across various components, rather than specific names like invoiceList or stockList, helps to identify patterns in your code design and allows you to consolidate boilerplate code into parent or helper classes.

4. Creating Shared Wrapper Components

A shared wrapper component serves as a presentational element utilized across multiple modules. This approach simplifies maintenance, future refactoring, and potential library migrations—such as transitioning from Kendo to AG Grid.

The primary advantage of a shared wrapper component, like one that handles a search box, is that any changes made within the wrapper will be reflected in all modules that utilize it. For example, implementing debounce functionality for search queries becomes straightforward in a reusable presenter.

In our ERP application, we utilized Kendo to create various grid types, including:

  • An inline edit-grid with row-by-row saving.
  • A batch grid.
  • A non-editable grid.

Each grid type was employed in multiple modules, and by consolidating shared logic within wrapper components for both batch-grid and edit-grid, I drastically reduced boilerplate code. Adding new features became significantly easier, requiring changes in just one location (the shared wrapper) rather than multiple components.

5. Leveraging Observable Data Services

To maintain focused component responsibilities and uphold the separation of concerns, we create services dedicated to data requests. Services can also function as stores in contemporary web applications.

Often, you can simplify your code complexity by avoiding the use of Redux, NgRx, or NgXs for application state management. Instead, we implemented BehaviorSubject-based services—known as Observable Data Services or Store Services—to handle the same tasks.

In each ERP module, we established two fundamental services:

  • An HTTP Service extending BaseHttpService, which contains methods for calling REST APIs.
  • A Store Service extending BaseStoreService, which incorporates BehaviorSubject variables and methods for calling the HTTP Service.

Think of a store service as an event bus that supports RxJs functional operators. It allows subscriptions with the subscribe() method and emits new values to subscribers via the next() method.

6. Implementing Callbacks

A callback in JavaScript is a function passed as an argument to another function. In our ERP, I utilized this technique for validations or logic executions following user interactions with the UI. Examples include:

  • Updating the unit-list drop-down options when the stock code changes.
  • Adjusting the "rate" column value when the currency type alters.

Our project involved dynamic forms, and the grid implementations were independent of the column lists. To manage this, I communicated callback functions through config objects—arrays that define specific columns or form fields for each component utilizing a dynamic form or grid.

7. Monitoring Performance

When new features are added, the application's behavior can shift, potentially leading to regressions or sluggish UI experiences. It's crucial to monitor performance, as it can significantly impact your application.

Tools like webpack-bundle-analyzer can be integrated into your package.json to assess your application bundle and dependency sizes.

Additionally, monitoring performance with Chrome DevTools is vital. You can capture heap snapshots to visualize memory distribution across your application's JavaScript objects, primitives, strings, functions, and DOM nodes. Identifying JavaScript heap memory leaks using the Memory panel is also essential.

8. Prioritizing Refactoring

Striking a balance between dynamic business needs and minimizing technical debt remains one of the most significant challenges developers face. To achieve this, we must adopt a culture of continuous refactoring as an integral part of our programming practice, ensuring clean code and design.

I resonate with Steven A. Lowe's perspective that refactoring is an opportunity to learn from coding experiences. It not only enhances code quality but also promotes a deeper understanding of the domain through improved models.

9. Adopting Evolutionary Architecture

Frontend architecture is not static; it evolves. Client needs and developer requirements will change over time, and processes that worked well in one project phase may need revisiting to enhance efficiency or reduce errors. A key skill for a frontend architect is the ability to adapt.

Collaborating with the team and adjusting designs concurrently with the development process fosters a better outcome. Gathering feedback from developers can provide fresh perspectives on your established code structure, highlighting areas for optimization.

10. Encouraging Team Feedback

A frontend developer's primary audience is the end user, while a frontend architect's audience is the development team. My role as a frontend architect did not mean stepping back from coding; in fact, I found myself writing more code to implement business features and meet my new audience's needs—my development team.

Implementing an enterprise application typically involves numerous contributors with varying technical skills. Understanding your team members' backgrounds assists in selecting suitable technologies, architectural patterns, and appropriate code complexity.

Throughout our ERP development cycle, we faced evolving requirements and a diverse team, ranging from junior to senior developers. One of our core objectives was to maintain low code complexity while supporting team members in their learning journey.

In conclusion, as a frontend architect, you engage in both frontend development and engineering. Your role encompasses designing technical solutions by selecting the most effective design patterns to address business and customer challenges while enhancing communication and collaboration.

Thank you for reading. I hope these insights prove beneficial in your journey.

The first video titled "What is Enterprise Architecture?" provides an overview of the concept and its significance in today's technology landscape.

The second video titled "Prescriptive Strategy of Enterprise Architecture" delves into the strategic aspects of enterprise architecture and its practical applications.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Jupiter Reclaims 'Moon King' Title from Saturn: A Cosmic Tug-of-War

Jupiter regains its title as 'Moon King' with 12 new moons, surpassing Saturn's count in an ongoing celestial rivalry.

Mastering Rust's if let: A Simplified Approach to Pattern Matching

Explore Rust's if let construct, a streamlined method for pattern matching that enhances code clarity and efficiency.

Feline Insights for Enhancing Self-Care Practices

Explore valuable self-care lessons inspired by our feline companions that enhance mental well-being.

Revolutionizing Cancer Diagnosis with AI Pathologists

Discover how AI is transforming cancer diagnosis through innovative methods that enhance accuracy and efficiency.

Exploring the Fascinating World of Snowflakes and Crystals

Discover the intriguing characteristics of snowflakes and crystals, their sizes, and the science behind them.

Mastering the Art of Prioritization: Strategies for Success

Discover effective strategies for prioritizing tasks when everything seems urgent, focusing on mindset and practical techniques.

Revolutionizing Icon Design with IconAI's AI-Powered Tool

Explore IconAI's innovative AI tool for generating icons that transform textual descriptions into visually captivating designs.

# A Comprehensive Guide to Managing Stress in Everyday Life

Explore effective strategies for understanding and coping with stress related to the past, present, and future.