rivernews / appl-tracky-spa

An Application Tracking System to help job finders ease their out-of-control spreadsheet use tracking every job application record.
https://appl-tracky.shaungc.com
1 stars 0 forks source link

Tabbed company lifecycle #72

Closed rivernews closed 3 years ago

rivernews commented 4 years ago

Problems

rivernews commented 4 years ago

After considering UPDATE company label, we found a major issue when visiting company detail page: we cannot locate the right bucket given only the uuid. This has to be solved by some other approaches. Due to the fact that too many troubles are found, we need to review the entire lifecycle of company, in order to figure out all changes required:

  1. [ ] Fetch company: login finished, then fetch in saga. After got jsonResponse, reflect in local redux by dispatching actions. If different company group has their own redux resource set, then dispatch all their actions.
  2. [ ] Create company: Button "Create Organization" --> Create company form --> submit form --> place the company in the right redux store bucket (company group, one of target, applied, interviewing and archived). Default label group to "Target".
  3. [ ] Update company: Edit company --> company update form --> make changes --> submit form --> remove company in current bucket, place in destination bucket.
  4. [ ] Visit company detail: in home page, click on a company. In the company detail page, pull out the company from redux by uuid. Currently does not support grouped company.

Review and audit

Current approach: standalone "buckets"

1. Fetch Each company group has its own saga, reducer and actions. Have to fire their action one by one in saga to fetch all of them. Lifecycle 1 will suffice automatically -- we just fire trigger action in auth saga, and the rest will be handled.

🕑 2. Create We will have to place new company in the right bucket. Default to Target. In company form, probably need a custom submit callback to dispatch the right action of right bucket. If not handled, will place all company in "All" bucket.

🕑 3. Update Handled: create in destination group bucket, and remove from current group bucket. However, if we want to change the structure of bucket store, this needs to be rewrite.

🕑 4. Visit Not handled currently. The company detail page is another route. We rely on only uuid to pass information between routes, and uses uuid to retrieve the company. Under the current company bucket store structure, we have no idea which bucket contains this uuid company. At worst have to traverse alll bucket, which is a no no.

5. Pagination Handled, since filter will work in their own url namespace.

If we go for this approach, we definitely need to remove the "All Company" tab and all its store, as it interferes with the mechanism, and can mislead that things work while in fact it's broken.

Proposed approach: single pool + indices group

This is to solve the Lifecycle 4 trouble, so we avoid having to find which bucket to look into. If we have a single pool -- single source of truth, we simply pull from this pool. No bucket even needed. What about update? Only matters in the home page, i.e. master page, level, where groups have to be in the right tab.

However we need to mind how pagination work in this approach. When each tab / each company group is fetching their company page(s), they all contribute to a single company pool. Use infinite scroll as an example, new pages will be reduced and merged into single pool. --> Looks like not much concern about this.

Let's review how this approach solves the CRUD operations.

rivernews commented 4 years ago

Remaining Issue: relational data lifecycle

When an application is updated, its redux update to application store. But not the "embed" application in company object. Same for all CRUD operations, and for applications & application status links.

rivernews commented 4 years ago

Proposed Solution to Relational Data Lifecycle

rivernews commented 4 years ago

Dealing with Normalization

Since we are doing normalization, we need to cover a wide range of saga handler, actually even all of them. The main challenge is having to accessing redux resource of other objects when in an object redux saga handler. Because in the entities, you might have multiple types of objects normalized and surfaced up.

Ideally, we want the factory to handle this rather than having multiple saga override/done handler. There are several ways to do this and we'll explore them here.

Manifest of normalization info in factory func arg

const reduxFactory = (..., normalizeManifest) => {
    reduxResources.saga = function*() => {
        const jsonResponse = await fetch(...);

        // TODO 1: check normalizeManifest arg availability

        const normalizedData = normalize(jsonResponse, normalizeManifest.schema);
        const objectNormalizeData = normalizedData.entity[normalizeManifest.objectEntityKey];
        const relationalNormalizeData = normalizeManifest.relationalEntityKeys.map(key => normalizedData.entity[key]);

        // deal with primary object actions
        if (crudKeyword == DELETE) {
            .... // same as existing
        }
        else {
            .... // same as existing
        }

        // deal with relational entities actions - for LIST/CREATE/UPDATE/DELETE - 
        // TODO 2: check  normalizeManifest availability
        for (let entityName of normalizeManifest.relationalEntityKeys) {
            // TODO 3: what kind of CRUD needs to be applied? May not be the same as the primary object
            const entitySuccessAction = normalizeManifest.actions[entityName][?crudKeyword?][RequestStatus.SUCCESS].action;
            const entityData = normalizedData.entities[entityName];
            // TODO 4: how to deal with single/list response?
            yield put entitySuccessAction(entityData / { results: entityData });
        }
    }
}

Discussion

Case study: company redux operations. Let's see its LIST/CREATE/UPDATE/DELETE scenarios.

Company LIST: ✅ Application | | | ✅Status For company, we should apply LIST/SUCCESS action(listResponse). For application, we should apply LIST/SUCCESS. For status (application status), we should also apply LIST/SUCCESS.

Company CREATE: ✅ Application | | | ✅Status For company, we should apply CREATE action(singleResponse). For application, we should apply CREATE action. For application status, we should apply CREATE action.

Company UPDATE: ✅ Application | | | ✅Status For company, we should apply UPDATE action(singleResponse). For application, we should apply nothing. -- The update is totally company's own business. For application status, we should apply nothing.

Company DELETE: ✅ Application | | | ✅Status | | | ✅ Cascade DELETE del application ⏸ Cascade DELETE del company For company, we should apply DELETE action(singleResponse, formData). For application, we should apply DELETE action(undefined, listResponse) < we may need to change DELETE action to support list (multiple) delete. For application status, we should apply DELETE action(undefined, listResponse)

Generalization for Application / Status? Yes, as long as the relationship is like Company (primary) <------ Application (relational), or primary <----- multiple relational, then this paradigm will suffice.

Challenge of Cascade Delete

We now choose to handle nested field in done saga, and have factory saga handle 1st level rellational data deletion.

The problem is, at the time of done saga, we lose company and application in store. While we have formData to deliver company data, we lose application in store. If we don't have application, we can not de-normalize statuses. Otherwise you'll have to do a O(n) filtering on status store.

rivernews commented 4 years ago

Dealing with Circular Dependency

While implementing Cascade DELETE, we found a circular dependency:

application-redux: ApplicationRestApiRedux --needs--> appliation-redux: ApplicationNormalizeManifest

application-redux: ApplicationRestApiRedux --gives--> application-redux: ApplicationActions (gives action, reducer, saga)

appliation-redux: ApplicationNormalizeManifest --needs--> application-normalize: ApplicationStatusActions --needs--> statuses-custom-saga: applicationStatusDoneCreateSuccessSagaHandler --needs--> application-redux: ApplicationActions


To summarize:

rivernews commented 3 years ago

68 et al., had completed this feature.