Spurtcommerce Dev Team is Live on Discord for instant support

dimg1 dimg2 dimg3 dimg4 dimg5 dimg0
white-mask Connect Now cls
×

By Introducing Key Elements of Angular Architecture

The Spurtcommerce team has put in every possible effort to make sure that all the key stones of Angular Architecture for the Front-End Development is in place.

The entire Front-End Architecture of Spurtcommerce has been built on Angular technology and it has been segregated into four layers.

angular-architecture

Configuration Layer

Here is where all the Application’s common components and variables are stored. This layer is mainly serving the purpose of a main home. Throughout the Application, wherever any component or variables needs to be re-used, they need not be written again and again in the hard code. Instead, they can be straight away fetched from the Configuration layer.

The configuration layer is further segregated into three layers - Environment, I18N and Shared Components. Let us now study how each layer has been used and for what purpose, in Spurtcommerce.

  • Environment - All the global constants will be mainly stored and handled from here. Also, the various environment files - local development, production, QA and UAT need not have separate configurations. There will be no need for re-writing any global constants or maintain separate files, for each of them. In, whichever environment the code needs to run, the environment can automatically take global constants from here.
  • 118N - This is mainly used for the multi-language feature in Spurtcommerce. All the applications generally have the static labels and captions and so it is with any eCommerce applications and Spurtcommerce also. For different labels, different equivalent terms can be given in multiple languages. All these will be stored in I18N and whenever needed, they can be derived from here.
  • Shared Components - Here, Application’s all the services will be maintained, say for example, pagination component, slider, ratings, etc. Wherever and whenever, such components need to be derived, it can be fetched from the Shared components layer.

Business Layer

The entire business logic of the Application can be derived from the Business Layer. Whatever data manipulations happen in the front end are all set up in Business layer.

Application’s Main Module

Under the Business Layer, we have the Application’s main module, that has several sub-modules. And, these sub-modules are classified into four - Actions, Reducer, Effect and State. These are taken from the NGRX Store Library. We have used NGRX Store for handling complex state from many different sources at a global application level.

  • Actions - Action are messages that are dispatched — to update the state in the store. In Spurtcommerce, different actions include Authentication, Catalog, Orders and so on.
  • Reducer - Reducer is pure function, it is roughly equivalent to table in the database in the way it provides a slice of data.
  • Effect - An Effect listens for actions dispatched from Store, performs operations and then dispatches new Action to Store.
  • State - The State is where all the data is handled.

Now, I shall give you one example of the use of these sub-modules in Spurtcommerce. For example, let us take ‘Product List’ – here the ‘Get Product List’ becomes an action, and for this supportive Reducer and Effect should also be there. We have provided the Sample Code for all these sub-modules below, in order to give you a detailed understanding.

Action


  export const ActionTypes = {
    GET_PRODUCT_LIST: type('[List] Do Product list'),
    GET_PRODUCT_LIST_SUCCESS: type('[List] Do Product list Success'),
    GET_PRODUCT_LIST_FAIL: type('[List] Do Product list Fail'),
  }

  // product list action
  export class GetProductListAction implements Action {
    type = ActionTypes.GET_PRODUCT_LIST;

    constructor(public payload: ProductListModel) {}
  }

  export class GetProductListSuccessAction implements Action {
    type = ActionTypes.GET_PRODUCT_LIST_SUCCESS;

    constructor(public payload: any) {}
  }

  export class GetProductListFailAction implements Action {
    type = ActionTypes.GET_PRODUCT_LIST_FAIL;

    constructor(public payload: any = null) {}
  }
  

Reducer


  case actions.ActionTypes.GET_PRODUCT_LIST: {
      return Object.assign({}, state, {
        listLoading: true,
        listLoaded: false,
        listFailed: false
      });
    }
    case actions.ActionTypes.GET_PRODUCT_LIST_SUCCESS: {
      let productModel = payload.data.map(_products => {
        const tempProductModel = new ProductListResponseModel(_products);
        return tempProductModel;
      });
      if (state.productDetail && Object.keys(state.productDetail).length) {
        const tempDetails = state.productDetail;
        if (tempDetails.relatedProductDetail && tempDetails.relatedProductDetail.length > 0) {
        productModel = productModel.filter(item1 =>
            !tempDetails.relatedProductDetail.some(item2 => (item2.productId === item1.productId)));
        }
      }
      return Object.assign({}, state, {
        listLoading: false,
        listLoaded: true,
        listFailed: false,
        productList: productModel
      });
    }
    case actions.ActionTypes.GET_PRODUCT_LIST_FAIL: {
      return Object.assign({}, state, {
        listLoading: false,
        listLoaded: false,
        listFailed: true
      });
    }
  

Effect


  @Effect()
  doProductLists$: Observable = this.action$.pipe(
    ofType(actions.ActionTypes.GET_PRODUCT_LIST),
    map((action: actions.GetProductlistAction) => action.payload),
    switchMap(state => {
      return this.service.productList(state).pipe(
        switchMap(product => [
          new actions.GetProductlistSuccessAction(product)
        ]),
        catchError(error => of(new actions.GetProductlistFailAction(error)))
      );
    })
  );
  

State


  export interface ProductState extends Map {
    productList: ProductListResponseModel;

    listLoading: boolean;
    listLoaded: boolean;
    listFailed: boolean;

  }

  export const ProductStateRecord = Record({
    productList: [],

    listLoading: false,
    listLoaded: false,
    listFailed: false,

  });
  

Async Services

In Spurtcommerce, wherever and whenever HTTP calls or API calls needs to be processed, they all come from Async Services. API calls for all the modules are written as separate services here.

Here’s a sample code block to depict how Async Services has been defined in Spurtcommerce.


  @Injectable()
  export class ProductService extends Api {
    // url
    private basUrl = this.getBaseUrl();

    /**
    * Handles 'productList' function. Calls get method with specific api address
    * along its param.
    *
    //  * @param  params from ProductListModel
    */
    public productList(params: ProductListModel): Observable {
      let reqOpts: any = {};
      reqOpts = params;
      return this.http.get(this.basUrl + '/product/productlist', {
        params: reqOpts
      });
    }

  }
  

Intermediate Layer

This is a layer that forms as a bridge between the UI Component Layer (Refer to the 4th Heading) and the Business Layer. Whenever a process needs to be established, say for example, if a data needs to be called by the UI Component layer, from the Business layer and also if any data needs to be retrieved from the UI Component layer to the Business layer, in any case and vice versa, then, this Intermediate layer acts as the bridge and a transmitter to transfer and retrieve data.

For example, if request for 10 products is there in the State, and this is called from the UI Component layer, then the definitions in the Intermediate layer help in fetching the data from the Business layer, to the UI component layer.

Let us now look at how the sample code can show this architecture in the best manner:


  @Injectable()
  export class ProductSandbox {
    public productList$ = this.appState.select(getProductList);

    public productListLoading$ = this.appState.select(ProductListLoading);
    public productListLoaded$ = this.appState.select(ProductListLoaded);
    public productListFailed$ = this.appState.select(ProductListFailed);

    private subscriptions: Array = [];

    constructor(
      protected appState: Store,
    ) {
      // ----
    }

    public getProductList(value) {
      this.appState.dispatch(
        new productActions.GetProductlistAction(new ProductListModel(value))
      );
    }      
  }  
  

UI Component Layer

This layer is the front most end, at the User level. This is where what Users visualizes whatever we have explained so far in this article.

What do we mean by UI Components?

A page is built with multiple components. Let us take one example of Home page in Spurtcommerce. It has several components – Header, Banner, Featured Products, Today’s deals, Footer and so on. These are the parent components. Under that, many child components will be there. For example, under Header, we will find logo, account information, Settings and likewise.

Wrapping Up:

In this manner, the Spurtcommerce team has adopted Angular 10 for the Application’s Front End Development. The team has designed the architecture by segregating the components and layers. The reason behind segregation is that a front-end team will have different expertise. Here are the following three expertise that works in a front-end team:

  • UI related experts
  • Logic building experts
  • Core libraries development experts

When such a team is working together, the architecture should be enough flexible, where one Expertise need not depend on another Expertise, while working. Different Expertise, should be able to work individually, without any dependency.