Cut to the thinking point

Componentization solves a set of reusable functions. We can use common open source components or precipitate business components that meet our own business according to our special business scenarios. We can use some open source scaffolding, such as VUE-CLI, create-React-app, or the company’s own internal scaffolding solutions. But who solves the logic that is scattered across modules and projects? How to avoid hard code programming, reduce the later maintenance and cost of logic, and so on, is also a point to consider.

To observe the code

First of all, we can analyze this code from an objective point of view. Reviewing this code, we can see many problems, such as:

  • The initial configuration parameters and type checking configuration take up a lot of code, can be removed to the configuration file management to maintain?
  • Whether tools classes can be refactored. Tools aggregates many types of auxiliary methods, whether the growth of tools will continue to be bloated, and whether Tools can be categorized and summarized
  • Can the internal tools of Tools be divided into a single task or multiple tasks?
  • Is it possible to split functions that are too long, increasing readability requirements?
  • Many methods rely on other methods of their own objects, the flow of the whole link is complex and changeable, pull a launch the whole body.
  • Code capabilities are not clearly delineated and generic and non-generic are not clearly defined
  • The code repetition of external exposure capability is relatively high
  • .

At that time, the initial writing of this code has also done a simple classification, a little logical management of the shallow consciousness. However, we can look at our own real production company’s project, multi-person maintenance, collaborative development, business growth, and so on, to the end has been completely uncontrollable, logic does not dare to move, only dare to patch, more and more bloated. The following is a piece of analysis I did earlier on a small piece of our internal project, which is a real pain point in almost everyone’s code.

  • Whether a single time processing function can be detached from the common logic, based on the attributes of the prototype chain, whether it will pollute and overwrite the attributes of the prototype chain, etc
  • Can business interaction design capabilities be encapsulated in separate functions?
  • Enumeration unified pull out management?
  • Request to withdraw unified management?
  • Data conversion assignment processing?
  • Complex text assembly, abstraction into functions, improve readability? Reduce complexity?
  • Can multiple logical judgments simplify expressions? Decompose complex conditions, merge behavior consistent?
  • .

What does the front end do to the business?

Based on the previous analysis of the code, a lot of questions piled up, indicating that this was indeed a sore point for us. So these pain points really boil down to what did we do to cause them? What does the front end actually do for the business?

  1. Capturing business data (capturing data under business rules)
  2. Data processing (subdivision, conversion, formatting, validation, etc.)
  3. Business judgment (for business scenarios, what needs to be done in each scenario)
  4. Business data submission (recording of data produced by business rules)
  5. Business interaction functionality (what needs to be done and what needs to be done under business rules)
  6. Business presentation (in business scenarios, show the business form reasonably)
  7. . (Just think of these areas for the moment, welcome to add if missing)

That’s pretty much everything the front end needs to do in the business, and it’s all our logic.

Deep thinking about logic

We need these logical piles to complete what we need. In fact, every small piece of business code is transferred step by step from the simplest logical rules to the final result we need. Just like the thinking brain diagram we make, every process node is a small logic. From the beginning of a business to the end of a business is made up of the smallest logical points.

So, can we look at the whole business from a global perspective, can we break every process node into a smallest atom, and all business logic is assembled from the smallest atom one by one, so that we can focus more on the smallest logic. Everything we do is made up of atoms. This allows you to hold any logic from the ground up, no matter how complex or simple.

We can also refer to the fact that in Java or any other backend language, design is initially ideal. They both want my world to be like the real world, a world where the smallest particles assemble the design I want. So a class stands for one kind of thing and a function stands for one thing. No matter how you play it up there, I can support you in building the world you want, any complicated thing you want to do. Therefore, logic processing is also like this, any logic into the smallest particles, through splicing, assembly, to support any business logic at the upper level.

After that, imagine the following scenario:

  1. Only care about atomic logic, enrich atomic logic
  2. Business logic, which ADAPTS any business rules to the logic provided by atoms, to produce any business code by assembly
  3. When service rules change, a logical node is directly replaced with a slot. Big changes, reassembling another line of business.
  4. The data flow of the whole link is clear and traceable
  5. .

Ideal design architecture diagram

Simply explore the design ideas

Atomic logic: The base class of an object that manages all injected atoms

Combinatorial logic: inherit atoms, combinatorial, output

External interface: parses configuration, calls atomic and composite class management, and throws production results

The idea diagram is as follows:

Base class design code

Class Atom {/* * inject Atom logic and manage it as an attribute * objArr: * */ setBasics(objArr) {objarr.foreach (x => {this[x.name.] = x.assembly})} /* * Make the atoms required by the assembly class * param * Type: String - specify one, Array - specify multiple, none (undefined) - all * * return * output: * */ machiningBasics(useBasics) {let output = {} if (useBasics) {if (array.isarray (useBasics)) { useBasics.forEach(x => { Object.assign(output, this[x]) }) } else { Object.assign(output, this[useBasics]) } } else { Object.keys(this).forEach(x => { Object.assign(output, this[x]) }) } return output } }Copy the code

The base class, as the base module at the lowest level, manages all the atoms for the upper level business logic to inherit and invoke to assemble its own business logic. The class throws two methods as follows:

SetBasics: As an injection of atomic logic. Can continue to enrich the underlying atomic logic (whether to support dynamic injection later, consider);

MachiningBasics: Provides logic for assembly classes to inherit atoms, output the underlying basics needed for upstream assembly

Assemble class design code

Param - * * return * Temporary: param - * * return * Temporary: Function makeObject() {function Temporary(assembly) {for (let key in assembly) {this[key] = Assembly [key]. Bind (this)}} return Temporary} /* * Temporary: param * config: Return * output: * */ function isThrough(Temporary, config) { Let temp = new Temporary(config.assembly) let output = {} for (let key in temp) {// Whether to enable the configuration if (config.through === If (temp. HasOwnProperty (key)) {output[key] = temp[key]}} else {output[key] = temp[key]}} Return output} // The assembly class manages the assembly and output. Class Package {/* * insert config * param * config: prototype: * * return The finished object * */ setPackage(config, prototype) { let temp = makeObject(config) temp.prototype = prototype return isThrough(temp, config) } } export default PackageCopy the code

The assembly class assembles the required pieces of business logic through a series of atomic logic. The overall steps are: the assembly object is produced, the assembly atom is inherited through the prototype, and the assembly result is exposed externally. It’s like a factory, producing targets, producing materials, producing products. The assembly class throws a method internally:

SetPackage: Assembles a class of business logic based on the provided configuration file and the atoms to be inherited.

Index entry Design

Import Atom from './ Atom /index' import Package from './ Package /index' // Instantiate Atom and assemble class const _atom = new Atom() const _package = new Package() let _globalCache = {} * */ export const injection = function (param) {_atom.setBasics(param.atom) param.package.forEach(x => {let) Prototype = _atom.machiningBasics(X.iraq) // GlobalCache [x.Iraq] = _package.setPackage(x, Prototype)})} /* * prototype; String - specify one, Array - specify multiple, undefined - all * * return * output: * */ export const getMateriel = function (param) {let output = {} if (param) {if (array.isarray (param)) { return param.forEach(x => { output[x] = _globalCache[x] }) } else { output = _globalCache[param] } } else { output = _globalCache } return output }Copy the code

The main function of the external entrance is to parse the configuration, assemble the configuration, and output the assembly result for use.

Injection: the standard external entry is used to initialize logic management. This method injects all atomic logic into the atomic class, and then obtains the atoms inherited by each assembly object for assembly through assembly configuration. Finally, the assembled logic is stored in a global cache.

GetMateriel: Output the assembly logic completed in production externally, expose the result of the end of assembly, obtain all assembly results, or obtain results individually or in batches

Use format

Default injection configuration (injection method)
Atom: [' atomic logic 1', 'atomic logic 2'], package: [' atomic logic 1',' atomic logic 2']}Copy the code
Atomic logic file format
/* * This format is a standard format for atomic logic. * name: atomic class name * assembly: object stored in atomic methods */ export default {name: 'atomic name ', assembly: SendRequest () {// do something}}}Copy the code
Assemble logical file formats
/* * This is a standard format for assembly logic * name: the name of the assembly class * extends: the atom from which the assembly class inherits * through: Whether to pass through information inside the atomic class * assembly: */ export default {name: 'Assembles class name ', extends:' extends atom ', // Supports string (single atom), none (inherits all atoms by default), array (specifies multiple atoms) assembly: {// Assembly logic output method, can be directly this. To call getAtom1Promise() {// do something... }}}Copy the code

NPM run start NPM run start

Directory format

– the SRC | – atom / / atomic logical location | – package / / assembly logic location | – index. Js file / / entry

Atomic Logic (ATOM)

atom1.js

// atom1.js
export default {
  name: 'atom1',
  assembly: {
    sendRequest() {
      return new Promise((res, rej) => {
        setTimeout(function () {
          res([1, 2, 3])
        }, 3000)
      })
    }
  }
}
Copy the code

atom2.js

// atom2.js
export default {
  name: 'atom2',
  assembly: {
    judgeArray(data) {
      return Array.isArray(data)
    }
  }
}
Copy the code
Package Assembly Logic

package1.js

// package1.js export default { name: 'package1', extends: 'atom1', assembly: {getAtom1Promise() {this.sendrequest ().then(x => {console.warn(' used successfully ', x)})}}Copy the code

package2.js

// package2.js
export default {
  name: 'package2',
  through: false,
  assembly: {
    packageLogin() {
      this.sendRequest()
        .then(x => {
          console.warn('判断是否是数组:', this.judgeArray(x))
        })
    }
  }
}
Copy the code
Entry (INDEX)

index.js

import {injection, getMateriel} from '@fines/factory-js' import atom1 from './atom/atom1' import atom2 from './atom/atom2' import package1 from './package/package1' import package2 from './package/package2' injection({ atom: [atom1, atom2], package: [package1, package2]}) console.warn(' Assembly successful: ', GetMateriel ()) // Test package1 method getMateriel('package1').getAtom1Promise() // Test package2 method getMateriel('package2').packageLogin()Copy the code
The test results

Making managed

address

Link: Portal feel reference can be clicked on the star, the interior is being used in the tread pit

Issues

Link: Portal problems, comments, you say 😆

The demo address

Link: Portal

PS: Can directly NPM run start directly run test

NPM release

The package name

@ fines/factory – js

The installation

NPM I @ fines/factory – js

Indicate the

Slighslighter is a newly registered organization that will write nice stuff, and all code that makes slighslighter stuff will be released to that package (more importantly, some of the package names are no longer usable, but organizations will be allowed unlimited).

Afterword.

I have made relevant explorations and thoughts in the field of logical management before, as follows:

  1. Think about writing more controlled code
  2. Explore the development and design of complex front-end services

On the basis of the previous exploration and deeper thinking, this logical solution was finally produced, which is only for your reference. We will continue to improve the solution in the future.

Some people in the community said, this is not your front end to do, not your work, do this why? After listening to this sentence, always feel a little uncomfortable.

In my opinion, each of us is an architect, constantly architecting our own code. Constantly to recognize the world as it is, to recognize yourself. We are all imperfect, some good and some bad. To find their own pain points, analyze the pain points, think, find out the final root, and then think about how to solve the pain points, try, grope, fail, stage victory, and then continue. So along the way, firmly believe that there will be a harvest. ‘!

Support information

Career Goal: Full stack architect Synchronous update: Blog Park, Zhihu, Zhihu column, Github