Skip to content

softwaregroup-bg/ut-microservice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

UnderTree standard microservice

Standard structure of UnderTree microservice

Creating microservices based on this template

This is used as a template to create new microservices.

In an empty folder named ut-* run:

npm init ut ms

Project links

Back end

Back end layers are defined in \index.js. It references various files from the following places:

πŸ“ ut-microservice
β”œβ”€β”€ build.js (build TS type definitions )
β”œβ”€β”€ config.js (default configurations)
β”œβ”€β”€ index.js (definition of layers)
β”œβ”€β”€ errors.js (error definitions loader)
β”œβ”€β”€ errors.json (error definitions)
β”œβ”€β”€πŸ“ api
|   β”œβ”€β”€πŸ“ lib (local reusable utility functions)
|   |   └── fn.js (utility function)
|   β”œβ”€β”€πŸ“ microservice (implementation of the API)
|   |   β”œβ”€β”€ index.js (index of all API functions)
|   |   β”œβ”€β”€ ...
|   |   └── microservice.object.predicate.js (API handler)
|   β””β”€β”€πŸ“ sql MSSQL definitions
|       β”œβ”€β”€πŸ“ schema (schema objects: tables, procedures, views, ... )
|       |   └── *.sql
|       β”œβ”€β”€πŸ“ seed (mandatory data: item types, actions, ...)
|       |   └── *.merge.yaml
|       β”œβ”€β”€πŸ“ standard (standard data used during automated tests)
|       |   └── *.merge.yaml
|       β”œβ”€β”€ schema.js (configuration for the schema folder)
|       β”œβ”€β”€ seed.js (configuration for the seed folder)
|       └── standard.js (configuration for the standard folder)
β”œβ”€β”€πŸ“ model (define data model and mocks)
|   β”œβ”€β”€ index.js (index of all models)
|   β”œβ”€β”€ mock.js (index of all mocks)
|   β”œβ”€β”€ ...
|   β”œβ”€β”€ foo.js (model definition)
|   └── foo.mock.js (mock definition)
β”œβ”€β”€πŸ“ server (back end test / debug)
|   β”œβ”€β”€ common.js (common configuration)
|   β”œβ”€β”€ index.js (microservice dependencies)
|   └── unit.js (unit test configuration)
β”œβ”€β”€πŸ“ test
|   β”œβ”€β”€πŸ“ jobs (definition of parallel jobs to run during tests)
|   |   β”œβ”€β”€ index.js (index of all jobs)
|   |   └── test.*.js (individual jobs)
|   β”œβ”€β”€πŸ“ steps
|   |   └── *.js (reusable test steps)
|   └── index.test.js (unit tests startup script)
β””β”€β”€πŸ“ validations (API definition)
    β”œβ”€β”€ index.js (index of all API definitions)
    └── microservice.object.predicate.js (a single API definition)

Front end

Front end is defined in the following folder structure:

πŸ“ ut-microservice
β”œβ”€β”€ ui.test.js (UT test startup script)
β”œβ”€β”€ πŸ“ browser
|   └── adminPortal.js (UI test portal entry)
β”œβ”€β”€ πŸ“ help (user guide content)
|   β”œβ”€β”€ _category_.yaml (title and index configuration)
|   β”œβ”€β”€ ...
|   └── microservice.tree.open.md (help pages)
β”œβ”€β”€ πŸ“ model (define data model and mocks here)
|   β”œβ”€β”€ index.js (index of all models)
|   β”œβ”€β”€ mock.js (index of all mocks)
|   β”œβ”€β”€ dropdown.js (dropdowns mock)
|   β”œβ”€β”€ ...
|   β”œβ”€β”€ bar.js (model definition)
|   └── bar.mock.js (mock definition)
β”œβ”€β”€ πŸ“ test
|   β””β”€β”€πŸ“ ui (UI tests)
|       β”œβ”€β”€ index.js (test runner)
|       β”œβ”€β”€ ...
|       └── microservice.bar.play.js (Playwright script)
└── πŸ“ portal
    β”œβ”€β”€πŸ“ backend (define optional backend handlers here)
    |   β”œβ”€β”€ index.js (index of all backend handlers)
    |   β”œβ”€β”€ ...
    |   └── microservice.object.predicate.js (backend handler)
    β”œβ”€β”€πŸ“ component (define UI components here)
    |  | index.js (index of all components)
    |  | ...
    |  β”” microservice.foo.open.js (a single component)
    β”œβ”€β”€πŸ“ handle (define reusable event handlers here)
    |  | index.js (index of all handlers)
    |  | ...
    |  β”” microservice.foo.click.js (a single handler)
    β”œβ”€β”€ config.js (configuration defaults)
    β”œβ”€β”€ index.js (layers)
    β”œβ”€β”€ index.stories.js (storybook)
    └── mock.js (mock loader)

Create a configuration file

Each microservice should be developed in a way, that enables debugging it without an implementation. The files needed to enable this are in the server folder. To connect to a database, a configuration file is needed, which should not be put in git. To avoid creation of a new configuration file for each microservice you work on, put a file named .ut_devrc in a common place (see ut-config for the possible places, where configuration can be placed). A second file named .ut_testrc is needed for the launch configurations which use set the environment variable UT_ENV=test.

The files must have the following recommended structure:

db:
  auto: true # this enables automatic DB creation based on user name
  logLevel: info
  connection:
    server: bgs-vws-db-10.softwaregroup-bg.com
    user: firstName.lastName # your first and last names
    password: xxx
    connectionTimeout: 60000
    requestTimeout: 60000
  create:
    user: # user, which can create databases
    password: # password of the above user
utLogin:
  login:
    expire:
      cookie: 400000000 # this makes the login not expire
      access: 400000000 # this makes the login not expire

Defining UI components

Components are created by following the pattern below:

import React from 'react';

/** @type { import("../../handlers").handlerFactory } */
export default ({
    import: {
        handle$microserviceFooClick,
        handleTabShow,
        component$subjectObjectPredicate
    }
}) => ({
    'microservice.foo.open': () => ({
        title: 'Foo edit',
        permission: 'microservice.foo.open',
        component: ({id}) => function FooOpen() {
            return (
                <div>
                    page content for item {id}
                </div>
            );
        }
    })
});

Components are defined in functions named using the subject.object.predicate pattern. These functions must return an object with the following properties:

  • title - A default title to be shown in the menu or other places in the UI.
  • permission - The permission to be checked to allow usage of the component.
  • component - Function which returns a React function component. This function can be async and can call to other front-end or back-end APIs before returning the React component.

Examples of recommended patterns for naming component functions:

  • microservice.foo.browse - For showing collection of foo items.
  • microservice.foo.new - For creating new foo items.
  • microservice.foo.open - For showing a single foo item for editing or viewing. Both editing and viewing is usually controlled through user's permissions and must be reflected in the respective UI elements. The component function (FooOpen) must receive a property id in the first argument, which is the identifier of the foo item. This id is also usually part of the URL or passed to the handleTabShow handler (see ut-portal docs for more info).

Defining event handlers

/** @type { import("../../handlers").handlerFactory } */
export default ({
    import: {
        subjectObjectPredicate
    }
}) => ({
    async 'microservice.foo.click'(params) {
        return {result: await subjectObjectPredicate({})};
    },
    'microservice.foo.clickReduce'({state, payload}) {
        return {...state, ...payload};
    }
});

Event handlers are defined in functions named using the subject.object.predicate or subject.object.predicateReduce patterns. The primary reason for having two handlers is the way Redux works, as it does not allow updating the state from async functions, while async functions are needed for interacting with the back end. If handling certain events does not involve updating Redux state, the reduce handler is not needed.

The reduce handler's first argument is an object with the properties:

  • state - Current Redux state.
  • payload - The result from the async handler.

This handler must return the new state, as per Redux rules.

Handler factories

Event handler functions and component functions are wrapped in handler factory functions, which have access to the UT framework API interface. The following destructuring patterns are available for use within the import property:

  • import:{subjectObjectPredicate} - Call back end methods.
  • import:{component$subjectObjectPredicate} - Use components defined elsewhere in the UI (in this or other modules).
  • import:{'component/subject.complexNaming':componentXxx} - Use components defined elsewhere in the UI (in this or other modules), which follow an arbitrary naming.
  • import:{handle$subjectObjectPredicate} - Use handlers defined elsewhere in the UI (in this or other modules).
  • import:{'handle/subject.complexNaming':handleXxx} - Use handlers defined elsewhere in the UI (in this or other modules), which follow an arbitrary naming.

Including in implementations

To include the module in an implementation:

  1. Add it as dependency in package.json:

    {
        "dependencies": {
            "utMicroservice": "^7.8.2"
        }
    }
  2. Add it to the list of modules in server/index.js:

    module.exports = (...params) => [{
        // other modules
    }, {
        main: require.resolve('ut-microservice'),
        pkg: require.resolve('ut-microservice/package.json')
    }, {
        // other modules
    }].map(item => [item, ...params]);
  3. Turn it on in server/common.js (if needed):

    module.exports = {
        // other settings
        utMicroservice: true
        // other settings
    }
  4. Add it to the list of modules in browser/xxxPortal.js

    module.exports = (...params) => [
        // other modules
        require('ut-microservice')(...params),
        // other modules
    ];
  5. Turn it on in browser/common.js (if needed):

    module.exports = {
        // other settings
        utMicroservice: {browser: true},
        // other settings
    }
  6. If you need to add items to a portal menu, check the ut-portal readme.

Microservice development tasks

Front end tasks

  1. Test UI components in Storybook, with React fast refresh, using mocked back-end:

    npm run storybook

    Edit ./portal/index.stories.js to:

    • match the developed components
    • add new mocked responses for the needed back end methods.
  2. Use Chromatic (see the project links above) to browse the published component library versions, documentation, visual testing, component screenshots, etc.

About

Standard UnderTree microservice structure

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •