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-services3-commons-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;
}
/src/interface/data/version1/BeaconV1.cs
namespace Beacons.Data.Version1
{
[DataContract]
public class BeaconV1 : IStringIdentifiable
{
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "site_id")]
public string SiteId { get; set; }
[DataMember(Name = "type")]
public string Type { get; set; }
[DataMember(Name = "udi")]
public string Udi { get; set; }
[DataMember(Name = "label")]
public string Label { get; set; }
[DataMember(Name = "center")]
public CenterObjectV1 Center { get; set; }
[DataMember(Name = "radius")]
public double Radius { get; set; }
}
}
/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,
}
}
/lib/data/version1/BeaconV1.dart
import 'package:pip_services3_commons/pip_services3_commons.dart';
class BeaconV1 implements IStringIdentifiable {
@override
String id;
String site_id;
String type;
String udi;
String label;
Map<String, dynamic> center; // GeoJson
double radius;
BeaconV1(
{String id,
String site_id,
String type,
String udi,
String label,
Map<String, dynamic> center,
double radius})
: id = id,
site_id = site_id,
type = type,
udi = udi,
label = label,
center = center, // GeoJson
radius = radius;
void fromJson(Map<String, dynamic> json) {
id = json['id'];
site_id = json['site_id'];
type = json['type'];
udi = json['udi'];
label = json['label'];
center = json['center']; // GeoJson
if (json['radius'] is int) {
radius = json['radius'].toDouble();
} else {
radius = json['radius'];
}
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
'id': id,
'site_id': site_id,
'type': type,
'udi': udi,
'label': label,
'center': center, // GeoJson
'radius': radius
};
}
}
/src/data/version1/BeaconV1.py
from pip_services3_commons.data import IStringIdentifiable
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";
}
/src/interface/data/version1/BeaconTypeV1.cs
namespace Beacons.Data.Version1
{
public class BeaconTypeV1
{
public static string Unknown = "unkown";
public static string AltBeacon = "altbeacon";
public static string iBeacon = "ibeacon";
public static string EddyStoneUdi = "eddystone-udi";
}
}
/data/version1/BeaconTypeV1.go
package data1
const Unknown = "unknown"
const AltBeacon = "altbeacon"
const IBeacon = "ibeacon"
const EddyStoneUdi = "eddystone-udi"
/lib/data/version1/BeaconTypeV1.dart
class BeaconTypeV1 {
static final unknown = 'unknown';
static final altBeacon = 'altbeacon';
static final iBeacon = 'ibeacon';
static final 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-services3-commons-node';
import { TypeCode } from 'pip-services3-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); }
}
/src/interface/data/version1/BeaconV1Schema.cs
namespace Beacons.Data.Version1
{
public class BeaconV1Schema : ObjectSchema
{
public BeaconV1Schema()
{
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.Double);
}
}
}
/data/version1/BeaconV1Schema.go
package data1
import (
cconv "github.com/pip-services3-gox/pip-services3-commons-gox/convert"
cvalid "github.com/pip-services3-gox/pip-services3-commons-gox/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
}
/lib/data/version1/BeaconV1Schema.dart
import 'package:pip_services3_commons/pip_services3_commons.dart';
class BeaconV1Schema extends ObjectSchema {
BeaconV1Schema() : super() {
withOptionalProperty('id', TypeCode.String);
withRequiredProperty('site_id', TypeCode.String);
withOptionalProperty('type', TypeCode.String);
withRequiredProperty('udi', TypeCode.String);
withOptionalProperty('label', TypeCode.String);
withOptionalProperty('center', null);
withOptionalProperty('radius', TypeCode.Float);
}
}
/src/data/version1/BeaconV1Schema.py
from pip_services3_commons.convert.TypeCode import TypeCode
from pip_services3_commons.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.