Advertisement

#16 Start Of Securities Domain Modeling With Markets And Segments

In this article we start by eliminating the implicit understanding of the shape of our state by requiring the use of string indexers whenever we want to access a property on that state. Once that is done we will define two new classes that represent the beginning of modeling securities within our application.

Changing the state constants

  • root
    • src
      • store
        • store-constants.ts

First up is changing to values of our routes and accounts state constants (a). Right now they are 'routes' and 'accounts' respectively and we carry around in the back of our minds the knowledge that they are chosen to be those values because we have properties on our state with those exact names. Instead of tacitly knowing what those values are we are going to make it explicit by changing the way access them.

store-constants.ts

...
export const STATE_ACCOUNTS = "STATE_ACCOUNTS";
export const STATE_ROUTES = "STATE_ROUTES";
(a) Changing the state constants to be more evocative of they way we want them used which is through a string indexer.

Using the state constants themselves to define the state interface

  • root
    • src
      • store
        • store-types.ts

Now instead of writing the property names themselves in our state interface we are changing things so that in order to access them we need to use the appropriate string indexer (b).

store-types.ts

...
import {
    STATE_ACCOUNTS,
    STATE_ROUTES,
} from "@/store/store-constants";

export interface IStoreState {
    [STATE_ACCOUNTS]: IAccountState;
    [STATE_ROUTES]: IRouteState;
}
...
(b) Updating our state interface to use string indexers to access the properties.

Fixing and modifying the account module

  • root
    • src
      • store
        • account-module.ts

With the changes that we have made to the state interface we now have to update our account module (c). In addition to fixing the way we access the properties we are also updating the names of the things that we are exporting and exporting the initial state itself. The point of changing the names and exporting the state is to make it both easier and more uniform to import into the store.

account-module.ts

...
import {
    ...
    STATE_ACCOUNTS,
} from "@/store/store-constants";
...
import { initialState } from "@/store/account-initial-state";
...
export const accountActions: StoreActionTree = {
    ...
};

export const accountMutations: StoreMutationTree = {
    [MUTATION_ADD_ACCOUNT](...) {
        const account = new AccountModel({ id: state[STATE_ACCOUNTS].index, name: payload });
        state[STATE_ACCOUNTS].items = [...state[STATE_ACCOUNTS].items, account].sort((a, b) => {
            ...
        });
        state[STATE_ACCOUNTS].index += 1;
    },
    [MUTATION_REMOVE_ACCOUNT](...) {
        state[STATE_ACCOUNTS].items = state[STATE_ACCOUNTS].items.filter((x) => x.id !== payload);
    },
};

export const accountState = {
    [STATE_ACCOUNTS]: initialState,
};
(c) Updating the account module to fix how we were accessing the account state as well as updating the exports.

Fixing and modifying the route module

  • root
    • src
      • store
        • route-module.ts

Next up we are going to mirror the changes that we made in the account module in the route module as well (d).

route-module.ts

...
import {
    ...
    STATE_ROUTES,
} from "@/store/store-constants";
...
import { initialState } from "@/store/route-initial-state";
...
export const routeActions: StoreActionTree = {
    ...
};

export const mutations: StoreMutationTree = {
    [MUTATION_POP_ROUTE](...) {
        switch (state[STATE_ROUTES].history.length) {
            ...
            case 1:
                state[STATE_ROUTES].history = [];
                return;
            default:
                state[STATE_ROUTES].history = state[STATE_ROUTES].history.slice(1);
                return;
        }
    },
    [MUTATION_PUSH_ROUTE](...) {
        state[STATE_ROUTES].history = [payload, ...state[STATE_ROUTES].history];
    },
};

export const routeState = {
    [STATE_ROUTES]: initialState,
};
(d) Mirroring the changes made in the account module here in the route module.

The store looks a lot cleaner and more organized now

  • root
    • src
      • store
        • store.ts

With the updates that we can now make to the store we can see the purpose of the changes and updates that we just made (e). To me things are now a lot cleaner and more organized.

store.ts

...
import {
    accountActions,
    accountState,
    accountMutations,
} from "@/store/account-module";

import {
    routeActions,
    routeState,
    routeMutations,
} from "@/store/route-module";
...
const actions = {
    ...accountActions,
    ...routeActions,
};

const mutations = {
    ...accountMutations,
    ...routeMutations,
};

const state: IStoreState = {
    ...accountState,
    ...routeState,
};
...
(e) Updating the store to reflect the changes made in the account and route modules.

The last string indexer for now

  • root
    • src
      • routing
        • routing-service.ts

The last thing we have to do before we can move on to something new is to update our route service to use the indexer that we specified when interacting with the route state (f).

routing-service.ts

...
import {
    IStoreState,
    STATE_ROUTES,
} from "@/store";
...
export class RoutingService {
    ...
    public back = () => {
        if (this._store.state[STATE_ROUTES].history.length === 0) {
            return;
        }
        const to = this._store.state[STATE_ROUTES].history[0];
        ...
    }
}
(f) Using the string indexer to access the route state within our routing service.

New state constants

  • root
    • src
      • store
        • store-constants.ts

Instead of jumping back and forth we are just going to take the time to specify a few new state constants that we are going to be needing shortly (g). These constants represent four new objects that we will be introducing to our state.

store-constants.ts

...
export const STATE_SECURITY_MARKETS = "STATE_SECURITY_MARKETS";
export const STATE_SECURITY_SEGMENTS = "STATE_SECURITY_SEGMENTS";
export const STATE_SECURITY_TERRITORIES = "STATE_SECURITY_TERRITORIES";
export const STATE_SECURITY_TYPES = "STATE_SECURITY_TYPES";
(g) Preemptively adding a few constants to represent some future additions to our state.
Advertisement

Enter the descriptor

  • root
    • src
      • store
        • security-descriptor.ts

For all of the objects that will be used in the states that are represented by the constants that we just added they will all, at least to start, consist of just an id and text property. Since this is the case, and to make life easier, we will start by defining an abstract class that they will all be able to inherit from (h). While we are here, in an effort to do a little future proofing, we will also define a method that we can call when we wish to persist our store to some long term storage such as a file on disk.

security-descriptor.ts

export abstract class SecurityDescriptor {
    public get id() { return this._id; }
    public get text() { return this._text; }

    constructor(private readonly _id: number, private readonly _text: string) { }

    public toPersistObj = () => {
        return {
            id: this._id,
            text: this._text,
        };
    }
}
(h) Defining a base class that several of our up coming classes can derive from.

The market

  • root
    • src
      • store
        • security-market-model.ts

The first descriptor that we will define is the security market (i). As I mentioned previously everything we currently need from the market is provide by the base class.

security-market-model.ts

import { SecurityDescriptor } from "@/store/security-descriptor";

export class SecurityMarketModel extends SecurityDescriptor { }
(i) The market descriptor defines where the security can be traded.

Time for more constants

  • root
    • src
      • store
        • security-constants.ts

The main purpose for the constants that we will be defining in the security constants file is to allow more consistent creation of our initial state (j).

security-constants.ts

export const SECURITY_MARKET_CBOE = "CBOE CONSOLIDATED";
export const SECURITY_MARKET_NASDAQ = "NASDAQ";
export const SECURITY_MARKET_NYSE = "NYSE";
(j) Adding constants that we can use in creating our initial states.

The market types

  • root
    • src
      • store
        • security-types.ts

The last thing that we have to do before creating the initial state is create the interface that will determine the shap of our market state within the overall store state (k).

security-types.ts

import {
    STATE_SECURITY_MARKETS,
    STATE_SECURITY_SEGMENTS,
    STATE_SECURITY_TERRITORIES,
    STATE_SECURITY_TYPES,
} from "@/store/store-constants";

import { SecurityMarketModel } from "@/store/security-market-model";

export interface ISecurityMarketsModelState {
    index: number;
    items: SecurityMarketModel[];
}

export interface ISecurityState {
    [STATE_SECURITY_MARKETS]: ISecurityMarketsModelState;
}
(k) Creating both the interface for the market state and adding it to the security state.

The market initial state

  • root
    • src
      • store
        • security-market-initial-state.ts

Not much to say here we just need to populate our market state with some initial data (l).

security-market-initial-state.ts

import { SecurityMarketModel } from "@/store/security-market-model";
import { ISecurityMarketsModelState } from "@/store/security-types";

import {
    SECURITY_MARKET_CBOE,
    SECURITY_MARKET_NASDAQ,
    SECURITY_MARKET_NYSE,
} from "@/store/security-constants";

const markets: SecurityMarketModel[] = [];

function createMarket(id: number, text: string) {
    markets.push(new SecurityMarketModel(id, text));
    return id += 1;
}

let index = 1;
index = createMarket(index, SECURITY_MARKET_NYSE);
index = createMarket(index, SECURITY_MARKET_NASDAQ);
index = createMarket(index, SECURITY_MARKET_CBOE);

export const initialState: ISecurityMarketsModelState = {
    index,
    items: markets,
};
(l) Adding some initial markets to our state.

The security segment

  • root
    • src
      • store
        • security-segment-model.ts

As expecting we just need to jump back in and follow the patter all over again. First up is creating the security segment model (m).

security-segment-model.ts

import { SecurityDescriptor } from "@/store/security-descriptor";

export class SecuritySegmentModel extends SecurityDescriptor { }
(m) Defining a security segment model.

Adding the segment constants

  • root
    • src
      • store
        • security-constants.ts

Just like with the markets we are adding some constants that we can use when creating the initial state (n).

security-constants.ts

export const SECURITY_SEGMENT_DEVELOPED_TEXT = "Developed Markets";
export const SECURITY_SEGMENT_EMERGING_TEXT = "Emerging Markets";
export const SECURITY_SEGMENT_LARGE_CAP_TEXT = "Large Cap";
export const SECURITY_SEGMENT_MID_CAP_TEXT = "Mid Cap";
export const SECURITY_SEGMENT_SMALL_CAP_TEXT = "Small Cap";
(n) The constants for the segment model.

Specifying the interfaces for the segment

  • root
    • src
      • store
        • security-types.ts

In our penultimate step for this article we need to define the interface for our segment state and add it to the security state (o).

security-types.ts

...
import { SecuritySegmentModel } from "@/store/security-segment-model";
...
export interface ISecuritySegmentModelState {
    index: number;
    items: SecuritySegmentModel[];
}

export interface ISecurityState {
    ...
    [STATE_SECURITY_SEGMENTS]: ISecuritySegmentModelState;
}
(o) Adding the necessary types for our segment state.

The segment initial state

  • root
    • src
      • store
        • security-segment-initial-state.ts

Once again time to define our an initial state (p).

security-segment-initial-state.ts

import {
    SECURITY_SEGMENT_DEVELOPED_TEXT,
    SECURITY_SEGMENT_EMERGING_TEXT,
    SECURITY_SEGMENT_LARGE_CAP_TEXT,
    SECURITY_SEGMENT_MID_CAP_TEXT,
    SECURITY_SEGMENT_SMALL_CAP_TEXT,
} from "@/store/security-constants";
import { SecuritySegmentModel } from "@/store/security-segment-model";
import { ISecuritySegmentModelState } from "@/store/security-types";

const segments: SecuritySegmentModel[] = [];

function createSegment(id: number, text: string) {
    segments.push(new SecuritySegmentModel(id, text));
    return id += 1;
}

let index = 1;
index = createSegment(index, SECURITY_SEGMENT_LARGE_CAP_TEXT);
index = createSegment(index, SECURITY_SEGMENT_MID_CAP_TEXT);
index = createSegment(index, SECURITY_SEGMENT_SMALL_CAP_TEXT);
index = createSegment(index, SECURITY_SEGMENT_DEVELOPED_TEXT);
index = createSegment(index, SECURITY_SEGMENT_EMERGING_TEXT);

export const initialState: ISecuritySegmentModelState = {
    index,
    items: segments,
};
(p) Adding the starting data to our segment initial state.
Exciton Interactive LLC
Advertisement