A Demo of Frontend Business Code Architecture in MVVM Pattern

After reading this article, you should have a deep understanding of the MVVM pattern. However, some may still have doubts, feeling that this architecture is somewhat idealistic. Others may perceive the content as theoretical and struggle with its practical implementation. In the following example, I will guide you through the step-by-step design of a code architecture using the MVVM pattern, using a hypothetical product development iteration to ensure a complete understanding of the MVVM pattern.

Iteration One: Membership Functionality

Let's take the example of implementing a membership feature for a supermarket, a business scenario that everyone is familiar with.

Requirements

Background:
  1. This is a medium-sized supermarket.
  2. The supermarket currently has a simple cash register system.
  3. The system involves roles such as the owner, cashier, and shop assistant.
Functional Requirements:
  1. Customers can become members of the store.
  2. Members can recharge with amounts of 100, 200, or 500dollar.
  3. When a member makes a purchase, a discount is applied based on the recharge amount: 5% for 100dollar, 10% for 200dollar, and 15% for 500dollar.
Business Process Analysis:
  1. Member Registration: Add a member registration entry to the system, click to enter the member registration page, enter information such as phone number and nickname, click confirm, and complete the registration.
  2. Recharge Function: Add a recharge entry to the system, click to recharge, enter the member's phone number, select the recharge amount, click confirm to create a recharge order, enter the existing payment process, and complete the recharge after successful payment.
  3. Consumption Function: Add a member card payment option to the existing payment page, click to enter the member's phone number, and complete the payment.

Design

Designing Models

Clearly, we should abstract a member model - MemberModel. Additionally, as it involves member management (creation in this case), abstract a member manager model - MemberManagerModel. To handle different amounts of recharge with varying discounts, abstract a recharge card model - CardModel (100dollar card, 200dollar card, 500dollar card). Also, abstract a card manager model - CardManagerModel. Lastly, as the recharge process involves placing an order, abstract an order model - OrderModel, and an order manager model - OrderManagerModel.

CardModel:

card
  Properties
    id
    balance // Balance
    discount // Discount
  Methods
    consume(amount) // Spend a certain amount of money
    recharge() // Recharge
    

CardManagerModel:

cardManager
  Properties
    cardList[]  // List of cards
  Methods
    listCard(params) // Request the list of cards
    createCard(cardInfo, memberId) // Create a card

MemberModel inherit cardManagerMoel

member
  Properties
    cardList[]  // List of cards
  Methods
    listCard(params) // Request the list of cards
    createCard(cardInfo, memberId) // Create a card
    

MemberManagerModel:

memberManager
  Properties
    memberList[]  // List of members
  Methods
    listMember(params) // Request the list of members
    createMember(memberInfo) // Create a member

OrderModel:

order
  Properties
    id
    type // Differentiates between recharge orders and product sales orders
    status // Status
    info // Sales information for the order, varies for different types of orders
    payInfo // Order payment information
  Methods
   getStatus(id) // Get the latest status
   getInfo(id) // Get complete order information

OrderManagerModel:

orderManager
   Properties
    orderList[]  // List of orders
   Methods
    listOrder(params) // Request the list of orders
    createOrder(info) // Create an order
Designing ViewModels

Designing viewModel directly based on the business process:

  1. CreateMember corresponds to the member registration process. Maintain an instance of MemberManagerModel and have a create method to call MemberManagerModel's createMember method for registration.
  2. Recharge corresponds to the recharge process: Maintain an instance of MemberModel, an instance of OrderManagerModel, and an instance of OrderModel.
    • Implement a method queryMemberInfo to accept a phone number as a parameter and call MemberModel's getInfo and listCard methods to retrieve the member's basic information and card list.
    • Implement an order method that accepts recharge amount and member ID as parameters, calls OrderManager's create order method, initializes the OrderModel instance with the returned order information.
    • Finally, implement a recharge method that accepts an amount parameter, determines the type of card to recharge, checks if the member has that type of card, initializes a CardModel with the card information, and executes its recharge method. If not, call MemberModel's createCard method to create a card, initialize the returned card information as a CardModel, and execute its recharge method.
  3. Consume corresponds to the consumption process: Maintain an instance of MemberManagerModel.
    • Implement a method queryMemberInfo to accept a phone number as a parameter and call MemberModel's getInfo and listCard methods to retrieve the member's basic information and card list.
    • Implement a consumption method that accepts an amount parameter and card type parameter, retrieves the corresponding card from the member's card list, initializes a CardModel, and executes the consumption method.
Designing Views

UI components:

Name Purpose Description
MemberInfo Display member information In objects/member/components
MemberForm Membership configuration form, including validation In objects/member/components
MemberChoice Select or input member In objects/member/components
CardInfo Display card In objects/card/components
CardList Display and select card from the list In objects/card/components
MoneySelector Amount selection In objects/card/components

Logical components:

  1. Create Member Component: Mainly displays MemberForm (membership configuration form). When the form is submitted, call the create method of CreateMember. Upon success, use the MemberInfo component to display member information; otherwise, display a prompt.
  2. Recharge Process Component, including a member selection page, amount selection page, and payment page.
    • The member selection page uses MemberChoice to select a member. Then call Recharge's queryMemberInfo to retrieve member information and proceed to the amount selection page.
    • The amount selection page uses MemberInfo to display member information, uses MoneySelector to select recharge amounts, adds a confirm button, and calls Recharge's order method when clicked. Upon success, proceed to the payment page, wait for payment completion using Recharge's orderModel's getStatus method, and then call Recharge's recharge method (excluding payment-related processes for simplification).
  3. Consumption Process Component
    • Use MemberChoice to select a member. Then call Consume's queryMemberInfo to retrieve member information and proceed to a verification page.
    • Display member information using MemberInfo, use CardList to display all member cards, and add a confirm button. After selecting a card and clicking the confirm button, call Consume's consumption method.

At this point, the membership and recharge functionality is complete, providing a clear and straightforward structure.

Iteration Two: Membership Password

Requirements

After the membership functionality is launched, the owner quickly identifies a security issue - the possibility of unauthorized use of the recharged amount by others. Therefore, the owner requests the addition of the following features:

  1. Members can set a password during registration.
  2. Members need to input their password before making a purchase.

Design

Changes to the model involve adding a password validation method - validatePassword to MemberManagerModel. Changes to the viewModel include adding a password field to the create method's parameters in CreateMember, and adding a method in Consume to call validatePassword for password validation. Changes to the view involve adding a password form item to the MemberForm component and adding a password input component to be used in the consumption process.

Iteration Three: Shop Assistant Incentives

Requirements

After the introduction of the membership feature, which increased user stickiness and repeat purchases, the owner is satisfied. However, the shop staff is not very enthusiastic about promoting memberships. Therefore, the owner wants to implement the following feature:

  1. Record the recommending staff during member registration and recharge. The owner will reward based on this information.

Design

Changes to the model involve adding an employee model (inheriting from MemberManagerModel) and an employee management model. Add employee ID parameters to the member registration and recharge order creation methods. Changes to the view involve adding an employee selection component and adding an employee selection form item in the member registration form and the recharge page.

Iteration Four: Client Mini Program (mini program is a software on wechat in China)

Requirements

The owner notices that other stores have a mini-program where members can view their cards and recharge amount by themselves. The owner wants to implement the following features:

  1. Members can view their cards and balances when they open the store's mini-program.
  2. Members can recharge themselves using the mini-program.

Design

Discovering that most of the mini-program's features have already been implemented in the existing model and viewModel files, you decide to reuse these files in both the mini-program and the cash register system. The following actions are taken:

Model layer remains unchanged.

Changes to viewModel involve adding a MemberInfo viewModel, maintaining a MemberModel, and implementing a getInfo method that calls MemberModel's getInfo and listCard methods.

View layer: Implement mini-program versions of MemberInfo, CardInfo, CardList, and other components. Implement a logic component for information display (MemberInfo), where CardList is used to display member information. Implement a recharge component, which is almost similar to the cash register system's recharge logic component, calling the Recharge viewModel too. The only difference is the absence of member selection interactions at the beginning, and payment uses the payment feature provided by WeChat.

Conclusion

Through the above example, it is expected that you understand how to design a code architecture using the MVVM pattern. If you were to design a backend to manage information such as members, cards, and orders, you would quickly realize that by reusing the existing models and developing the corresponding view and viewModel, you could efficiently develop the backend.

At the same time, you may deeply appreciate the advantages of this pattern. Code developed using it is highly scalable, with code reuse being straightforward, leading to an increasing development speed.

Moreover, you may realize that this development pattern heavily relies on your understanding of the business. The business in the above example is common and easy to understand. However, for some specialized businesses, as a frontend developer, you may not have a deep understanding. Therefore, it is essential to discuss and understand the business with the product manager or backend developer during development. Another direct approach is to ask the backend developer for the data model they have designed, this usually is a reliable way to design the model; if you can't get, you can refer to the database's table structure to abstract the data model.