AngularJS: Understanding design pattern

前端 未结 5 1608
逝去的感伤
逝去的感伤 2020-11-28 17:17

In the context of this post by Igor Minar, lead of AngularJS:

MVC vs MVVM vs MVP. What a controversial topic that many developers

5条回答
  •  感情败类
    2020-11-28 17:49

    I believe Igor's take on this, as seen in the quote you have provided, is just the iceberg tip of a far greater problem.

    MVC and its derivatives (MVP, PM, MVVM) are all good and dandy within a single agent, but a server-client architecture is for all purposes a two-agent system, and people are often so obsessed with these patterns that they forget that the problem at hand is far more complex. By trying to adhere to these principles they actually end up with a flawed architecture.

    Let's do this bit by bit.

    The guidelines

    Views

    Within Angular context, the view is the DOM. The guidelines are:

    Do:

    • Present scope variable (read only).
    • Call the controller for actions.

    Don't:

    • Put any logic.

    As tempting, short, and harmless this looks:

    ng-click="collapsed = !collapsed"
    

    It pretty much signify any developer that now to understand how the system work they need to inspect both the Javascript files, and the HTML ones.

    Controllers

    Do:

    • Bind the view to the 'model' by placing data on the scope.
    • Respond to user actions.
    • Deal with presentation logic.

    Don't:

    • Deal with any business logic.

    The reason for the last guideline is that controllers are sisters to views, not entities; nor they are reusable.

    You could argue that directives are reusable, but directives too are sisters to views (DOM) - they were never intended to correspond to entities.

    Sure, sometimes views represent entities, but that's a rather specific case.

    In other words, controllers shall focus on presentation - if you throw business logic in, not only you are likely to end up with an inflated, little-manageable controller, but you also violate the separation of concern principle.

    As such, controllers in Angular are really more of Presentation Model or MVVM.

    And so, if controllers shouldn't deal with business logic, who should?

    What is a model?

    Your client model is often partial and stale

    Unless you are writing an offline web application, or an application that is terribly simple (few entities), you client model is highly likely to be:

    • Partial
      • Either it doesn't have all entities (like in the case of pagination)
      • Or it doesn't have all the data (like in the case of pagination)
    • Stale - If the system has more than one user, at any point you can't be sure that the model the client holds is the same as the one the server hold.

    The real model must persist

    In traditional MCV, the model is the only thing being persisted. Whenever we talk about models, these must be persisted at some point. Your client may manipulate models at will, but until the roundtrip to the server was completed successfully, the job ain't done.

    Consequences

    The two points above should serve as a caution - the model your client holds can only involve a partial, mostly simple business logic.

    As such, it is perhaps wise, within client context, to use lowercase M - so it's really mVC, mVP, and mVVm. The big M is for the server.

    Business logic

    Perhaps one of the most important concepts about business models is that you can subdivide them to 2 types (I omit the third view-business one as that's a story for another day):

    • Domain logic - aka Enterprise business rules, the logic that is application-independent. For example, give a model with firstName and sirName properties, a getter like getFullName() can be considered application-independent.
    • Application logic - aka Application business rules, which is application specific. For instance, error checks and handling.

    It is important to stress that both of these within a client context are not 'real' business logic - they only deal with the portion of it that is important for the client. Application logic (not domain logic) should have the responsibility of facilitating communication with the server and most user interaction; while the domain logic is largely small-scale, entity-specific, and presentation-driven.

    The question still remains - where do you throw them within an angular application?

    3 vs 4 layer architecture

    All these MVW frameworks use 3 layers:

    Three circles. Inner - model, middle - controller, outer - view

    But there are two fundamental issues with this when it comes to clients:

    • The model is partial, stale and doesn't persist.
    • No place to put application logic.

    An alternative to this strategy is the 4 layer strategy:

    4 circles, from inner to outer - Enterprise business rules, Application business rules, Interface adapters, Frameworks and drivers

    The real deal here is the application business rules layer (Use cases), which often goes amiss on clients.

    This layer is realised by interactors (Uncle Bob), which is pretty much what Martin Fowler calls an operation script service layer.

    Concrete example

    Consider the following web application:

    • The application shows a paginated list of users.
    • The user clicks 'Add user'.
    • A model opens with a form to fill user details.
    • The user fills the form and hit submit.

    A few things should happen now:

    • The form should be client-validated.
    • A request shall be sent to the server.
    • An error shall be handled, if there is one.
    • The user list may or may not (due to pagination) needs updating.

    Where do we throw all of this?

    If your architecture involves a controller that calls $resource, all of this will happen within the controller. But there is a better strategy.

    A proposed solution

    The following diagram shows how the problem above can be solve by adding another application logic layer in Angular clients:

    4 boxes - DOM points to Controller, which points to Application logic, which points to $resource

    So we add a layer between controller to $resource, this layer (lets call it interactor):

    • Is a service. In the case of users, it may be called UserInteractor.
    • It provide methods corresponding to use cases, encapsulating application logic.
    • It controls the requests made to the server. Instead of a controller calling $resource with free-form parameters, this layer ensure that requests made to the server return data on which domain logic can act.
    • It decorates the returned data structure with domain logic prototype.

    And so, with the requirements of the concrete example above:

    • The user clicks 'Add user'.
    • The controller asks the interactor for a blank user model, the is decorated with business logic method, like validate()
    • Upon submission, the controller calls the model validate() method.
    • If failed, the controller handles the error.
    • If successful, the controller calls the interactor with createUser()
    • The interactor calls $resource
    • Upon response, the interactor delegates any errors to the controller, which handles them.
    • Upon successful response, the interactor ensures that if needed, the user list updates.

提交回复
热议问题