Created
September 19, 2017 15:12
-
-
Save freddi301/5219bd4a190227e812b191931c759195 to your computer and use it in GitHub Desktop.
version aware programming proof of concept
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
/// as first we declare the contract our program will have with external world | |
type v1Api = { | |
getPerson(id: string): v1Person, | |
addPerson(person: v1Person): void | |
}; | |
type v1Person = { id: string, name: string, age: number }; | |
/// let's see what happens if we add some capability to our api | |
type v2Api = { | |
getPerson(id: string): v2Person, | |
addPerson(person: v2Person): void, | |
allPerson(): Array<v2Person> | |
}; | |
type v2Person = v1Person; | |
/// let's leverage the typechecker (compiler in compiled typed languages) to check if our api is retrocompatible | |
/// trying to assign an instance of the new version to the old will do the trick 1* | |
declare var v1ApiControlIntance: v1Api; | |
declare var v2ApiControlIntance: v2Api; | |
v1ApiControlIntance = v2ApiControlIntance; | |
/// let's break the api and see how the compiler can be useful detecting the incompatibilities | |
type v3Api = { | |
getPerson(id: string): v3Person, | |
addPerson(person: v3Person): void | |
}; | |
type v3Person = v2Person; // 2* | |
declare var v3ApiControlInstance: v3Api; | |
/// you can see here how the compiler complains | |
// $ExpectError - erase this line to see the error | |
v2ApiControlIntance = v3ApiControlInstance; // 'property `allPerson` (Property not found in object type)' | |
/// lets'see how we can retain runtime data reloading | |
const v1ApiInstance: v1Api & { people: { [key: string]: v1Person } } = { | |
people: {}, | |
getPerson(id: string): v1Person { | |
return this.people[id]; | |
}, | |
addPerson(person: v1Person): void { | |
this.people[person.id] = person; | |
} | |
}; | |
const v2ApiInstance: v2Api & { people: { [key: string]: v2Person } } = { | |
people: Object.assign({}, v1ApiInstance.people), // migration here 3* | |
getPerson(id: string): v1Person { | |
return this.people[id]; | |
}, | |
addPerson(person: v1Person): void { | |
this.people[person.id] = person; | |
}, | |
allPerson(): Array<v2Person> { | |
return this.people; | |
} | |
}; | |
// example workflow | |
let entryPoint: any; // 4* | |
entryPoint = v1ApiInstance; // deployed version 1 | |
(entryPoint: v1Api).addPerson({ id: '1', name: 'fred', age: 23 }); // using version 1 | |
entryPoint = v2ApiInstance; // deployed version 2 | |
(entryPoint: v2Api).allPerson(); // using version 2 | |
/// let's see how we can achieve safe data migration | |
// first we need our v3 api instance | |
const v3ApiInstance: v3Api & { people: { [key: string]: v3Person } } = { | |
people: {}, | |
getPerson(id: string): v3Person { | |
return this.people[id]; | |
}, | |
addPerson(person: v3Person): void { | |
this.people[person.id] = person; | |
} | |
}; | |
// then declare v4 api type | |
type v4Person = { id: string, name: string, age: number, birth: Date }; | |
type v4Api = { | |
getPerson(id: string): v4Person, | |
// $ExpectError | |
addPerson(person: v4Person): void // the error is reported here because v3Person is not compatible with v4Person | |
}; | |
declare var v4ApiControlInstance: v4Api; | |
// the error is reported on the v4Api declaration | |
v3ApiControlInstance = v4ApiControlInstance; | |
const v4ApiInstance: v4Api & { people: { [key: string]: v4Person } } = { | |
// here is our simple migration method that iterates our previous repository and populates the new one | |
people: Object.keys(v3ApiInstance.people).reduce((people: { [key: string]: v4Person }, id) => { | |
people[id] = v3PersonTov4Person(v3ApiInstance.people[id]); | |
return people; | |
}, {}), | |
getPerson(id: string): v4Person { | |
return this.people[id]; | |
}, | |
addPerson(person: v4Person): void { | |
this.people[person.id] = person; | |
} | |
}; | |
// our adapter function | |
function v3PersonTov4Person(person: v3Person): v4Person { | |
return Object.assign({}, person, { birth: new Date(new Date() - 10 * 60 * 60 * 24 * 365) }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment