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
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"
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)
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.