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-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

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";
}

/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"

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

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.