Step 3. Data model development

We’ll start the development of our microservice with defining and implementing the data model that it is going to be working with. We’ll start by adding some folders to our project’s directory structure. In the src folder, create a data folder and, inside it, a version1 folder. This is done to allow us to create new versions of the data model later on, without breaking the old one.

Now, in the version1 folder, create a BeaconV1 class that implements IStringIdentifiable. By implementing the IStringIdentifiable interface, we can be sure that all objects of that given class have a string key by which they can be identified. Below is a listing of this class’s code:

/src/data/version1/BeaconV1.ts

import { IStringIdentifiable } from 'pip-services4-data-node';

export class BeaconV1 implements IStringIdentifiable {
    public id: string;
    public site_id: string;
    public type?: string;
    public udi: string;
    public label?: string;
    public center?: any; // GeoJson
    public radius?: number;
}



/data/version1/BeaconV1.go

package data1

type BeaconV1 struct {
	Id     string     `json:"id" bson:"_id"`
	SiteId string     `json:"site_id" bson:"site_id"`
	Type   string     `json:"type" bson:"type"`
	Udi    string     `json:"udi" bson:"udi"`
	Label  string     `json:"label" bson:"label"`
	Center GeoPointV1 `json:"center" bson:"center"` // GeoJson
	Radius float32    `json:"radius" bson:"radius"`
}

func (b BeaconV1) Clone() BeaconV1 {
	return BeaconV1{
		Id:     b.Id,
		SiteId: b.SiteId,
		Type:   b.Type,
		Udi:    b.Udi,
		Label:  b.Label,
		Center: b.Center.Clone(),
		Radius: b.Radius,
	}
}


/src/data/version1/BeaconV1.py

from pip_services4_data.data import IStringIdentifiable
from typing import Any

class BeaconV1(IStringIdentifiable):
    def __init__(self, id: str = None, site_id: str = None, type: str = None, udi: str = None, label: str = None, center: Any = None, radius: float = None):
        super(BeaconV1, self).__init__()
        self.id = id
        self.site_id = site_id
        self.type = type
        self.udi = udi
        self.label = label
        self.center = center
        self.radius = radius
Not available

All fields are of simple data types, and their names give us a good idea of their purpose. The only exception to this is the center field, in which we are going to be storing data of type GeoJSON. The beacon’s type will be represented by a string, but we’re going to have a separate class be responsible for managing the available types, using static fields. This class is going to be called BeaconTypeV1, and it’s going to simply contain a list of beacon types:

/src/data/version1/BeaconTypeV1.ts

export class BeaconTypeV1 {
    public static Unknown: string = "unknown";
    public static AltBeacon: string = "altbeacon";
    public static iBeacon: string = "ibeacon";
    public static EddyStoneUdi: string = "eddystone-udi";
}



/data/version1/BeaconTypeV1.go

package data1

const Unknown = "unknown"
const AltBeacon = "altbeacon"
const IBeacon = "ibeacon"
const EddyStoneUdi = "eddystone-udi"


/src/data/version1/BeaconTypeV1.py

class BeaconTypeV1:
    Unknown = "unknown"
    AltBeacon = "altbeacon"
    iBeacon = "ibeacons"
    EddyStoneUdi = "eddystone-udi"
Not available

For checking the validity of the data we are going to be receiving, let’s create a data validation schema in a class called BeaconV1Schema:

/src/data/version1/BeaconV1Schema.ts

import { ObjectSchema } from 'pip-services5-data-node';
import { TypeCode } from 'pip-services4-commons-node';

export class BeaconV1Schema extends ObjectSchema { 
    public constructor()
    {
        super();
        this.withOptionalProperty('id', TypeCode.String);
        this.withRequiredProperty('site_id', TypeCode.String);
        this.withOptionalProperty('type', TypeCode.String);
        this.withRequiredProperty('udi', TypeCode.String);
        this.withOptionalProperty('label', TypeCode.String);
        this.withOptionalProperty('center', null);
        this.withOptionalProperty('radius', TypeCode.Float);    }
}



/data/version1/BeaconV1Schema.go

package data1

import (
	cconv "github.com/pip-services4/pip-services4-go/pip-services4-commons-go/convert"
	cvalid "github.com/pip-services4/pip-services4-go/pip-services4-data-go/validate"
)

type BeaconV1Schema struct {
	cvalid.ObjectSchema
}

func NewBeaconV1Schema() *BeaconV1Schema {
	c := BeaconV1Schema{}
	c.ObjectSchema = *cvalid.NewObjectSchema()

	c.WithOptionalProperty("id", cconv.String)
	c.WithRequiredProperty("site_id", cconv.String)
	c.WithOptionalProperty("type", cconv.String)
	c.WithRequiredProperty("udi", cconv.String)
	c.WithOptionalProperty("label", cconv.String)
	c.WithOptionalProperty("center", cconv.Map)
	c.WithOptionalProperty("radius", cconv.Double)
	return &c
}


/src/data/version1/BeaconV1Schema.py

from pip_services4_commons.convert.TypeCode import TypeCode
from pip_services4_data.validate.ObjectSchema import ObjectSchema


class BeaconV1Schema(ObjectSchema):
    def __init__(self):
        super(ObjectSchema, self).__init__()

        self.with_optional_property("id", TypeCode.String)
        self.with_required_property("site_id", TypeCode.String)
        self.with_optional_property("type", TypeCode.String)
        self.with_required_property("udi", TypeCode.String)
        self.with_optional_property("label", TypeCode.String)
        self.with_optional_property("center", TypeCode.Map)
        self.with_optional_property("radius", TypeCode.Float)
Not available

Let’s take a closer look at what’s going on in this class. First and foremost, we create a new class that extends the standard validation class Schema, implemented in the components module of the Pip.Services Toolkit. This class contains all of the functions that we need for checking the validity of the data we receive. All that we have to do is state which fields we are expecting, what their types should be, and whether or not any of them are required. All of this is done in the class’s constructor.

Since everything we’ve done so far is quite simple and transparent, we’re not going to be writing any tests yet for the data model we’ve created.

With our data model defined, we can now move on to Step 4. Implementing persistence components.

Step 4. Implementing persistence components.