All Projects → mcgizzle → oh-migrations

mcgizzle / oh-migrations

Licence: Apache-2.0 license
Data migrations through implicit function composition at the type-level

Programming Languages

scala
5932 projects

Projects that are alternatives of or similar to oh-migrations

alphabet-soup
Type calculations at compile time
Stars: ✭ 40 (+73.91%)
Mutual labels:  typelevel, typelevel-programming
elm-collage
Create interactive vector graphics and position them relative to each other
Stars: ✭ 57 (+147.83%)
Mutual labels:  composition
clean-code-javascript-ko
🛁 Clean Code concepts adapted for JavaScript - 한글 번역판 🇰🇷
Stars: ✭ 1,767 (+7582.61%)
Mutual labels:  composition
do
Simplest way to manage asynchronicity
Stars: ✭ 33 (+43.48%)
Mutual labels:  composition
Edward-the-App
Write your first novel with the world's most helpful writing tool. (Out of business as of Dec 2021)
Stars: ✭ 55 (+139.13%)
Mutual labels:  composition
aurelia-knockout
Adds support for Knockout binding syntax to make transition from Durandal and Knockout to Aurelia simpler.
Stars: ✭ 22 (-4.35%)
Mutual labels:  composition
vue2-helpers
🔧 A util package to use Vue 2 with Composition API easily
Stars: ✭ 64 (+178.26%)
Mutual labels:  composition
SFDX-Data-Move-Utility-Desktop-App
This repository contains the special Desktop GUI Application, that will help you to prepare and execute data migration packages using the SFDMU Plugin.
Stars: ✭ 65 (+182.61%)
Mutual labels:  data-migration
go-errors
A super tiny package for error encapsulation in idiomatic Go
Stars: ✭ 14 (-39.13%)
Mutual labels:  composition
kaop
Advanced OOP Library with createClass, inheritance, providers, injectors, advices which enables handy Inversion of Control techniques
Stars: ✭ 40 (+73.91%)
Mutual labels:  composition
general
Repository for general Typelevel information, activity and issues
Stars: ✭ 19 (-17.39%)
Mutual labels:  typelevel
HoaLibrary-Max
🔉 HoaLibrary for Max
Stars: ✭ 70 (+204.35%)
Mutual labels:  composition
ftor
ftor enables ML-like type-directed, functional programming with Javascript including reasonable debugging.
Stars: ✭ 44 (+91.3%)
Mutual labels:  composition
rocket-pipes
Powerful pipes for TypeScript, that chain Promise and ADT for you 🚌 -> ⛰️ -> 🚠 -> 🏂 -> 🚀
Stars: ✭ 18 (-21.74%)
Mutual labels:  composition
gamma
An Eclipse-based modeling framework for the component-based design and analysis of reactive systems
Stars: ✭ 21 (-8.7%)
Mutual labels:  composition
typelevel-stack.g8
📚 Unofficial Giter8 template for the Typelevel Stack (Http4s / Doobie / Circe / Cats Effect / Fs2) based on Cats v1.x.x
Stars: ✭ 63 (+173.91%)
Mutual labels:  typelevel
vesselize
⛵ A JavaScript IoC container that works seamlessly with Vue.js and React.
Stars: ✭ 22 (-4.35%)
Mutual labels:  composition
reassemble
Fast Library for the Composition of React Higher-Order-Components
Stars: ✭ 67 (+191.3%)
Mutual labels:  composition
sbt-org-policies
Just an SBT plugin that enforces some organizational policies and settings across all of your projects
Stars: ✭ 22 (-4.35%)
Mutual labels:  typelevel
rustfst
Rust re-implementation of OpenFST - library for constructing, combining, optimizing, and searching weighted finite-state transducers (FSTs). A Python binding is also available.
Stars: ✭ 104 (+352.17%)
Mutual labels:  composition

oh-migrations

CircleCI Maven Central License

Data migrations through implicit function composition at the type-level.

This library provides the ability to version, decode and migrate data types.

Install

For core functionality add the following to your build.sbt:

"io.github.mcgizzle" %% "oh-migrations-core" % "<version>"

to interop with circe add:

"io.github.mcgizzle" %% "oh-migrations-circe" % "<version>"

to interop with Argonaut add:

"io.github.mcgizzle" %% "oh-migrations-argonaut" % "<version>"

versions can be found in the releases section.

Usage

Lets say you have a stringly typed User data type.

case class UserV1(firstName: String, lastName: String)

You then decide to add some type information through the use of value classes.

case class FirstName(value: String)
case class LastName(value: String)
case class UserV2(firstName: FirstName, lastName: LastName)

Next it is decided that we need a way to uniquely identify Users through a UUID, and while we are at it, combine the names.

import java.util.UUID

case class Name(value: String)
case class UserV3(name: Name, id: UUID)

Versioned

We must version all our data types in the chain by implementing the Versioned typeclass.

import shapeless.nat._

trait User
object User {
  implicit val v1 = Versioned[User, _1, UserV1]
  implicit val v2 = Versioned[User, _2, UserV2]
  implicit val v3 = Versioned[User, _3, UserV3]
}

MigrationFunction

Now we provide migrations from n to n + 1. This is a way to go from the current data type to the next version.

implicit val m1: UserV1 +=> UserV2 = u1 => 
  UserV2(FirstName(u1.firstName), LastName(u1.lastName))

implicit val m2: UserV2 +=> UserV3 = u1 => 
  UserV3(Name(u1.firstName.value + " " + u1.lastName.value), getUUID)

MigrationFunctionF

Sometimes it may be necessary to migrate our data types effectfully.

implicit val m1: MigrationFunctionF[F, UserV1, UserV2] = u1 => 
  UserV2(FirstName(u1.firstName), LastName(u1.lastName))

implicit val m2: MigrationFunctionF[F, UserV2, UserV3] = u1 => 
  getUUID.map( uuid => UserV3(Name(u1.firstName.value + " " + u1.lastName.value), uuid))

Migrate

Here we get our migration composition 1 -> 3 for free

val u1 = UserV1("Frederick", "Wiley")
Migrate[User].from[_1, _3].apply(u1) == UserV3(Name("Frederick Wiley"), getUUID)

DecodeAndMigrate

The DecodeAndMigrate typeclass provides the ability to attempt to decode our User from the latest version and then migrate it to our desired version. As long as we have defined Versioned, MigrationFunction and Decoder for all our versions, we get the following for free.

// We provide Decoders for each version of User
implicit val d1: Decoder[String, UserV1] = Decoder.from(_ => Left(DecodeFailure("failed on d1")))   
implicit val d2: Decoder[String, UserV2] = Decoder.from(_ => Right(UserV2(FirstName("Decoded"), LastName("By UserV2"))))   
implicit val d3: Decoder[String, UserV3] = Decoder.from(_ => Left(DecodeFailure("failed on d3")))   

// It decodes a UserV2 as it is the latest available and then migrates it to UserV3
DecodeAndMigrate[User].from[String, _1, _3]("{ json value for example}") shouldBe Right(UserV3(Name("Decoded By UserV2")))

Interop

This functionality can be easily interoped with circe using oh-migrations-circe.

import io.circe._
import io.circe.generic.auto._
import io.github.mcgizzle.circe._

val json = UserV2(FirstName("Decoded"), LastName("By Circe")).asJson
DecodeAndMigrate[User].from[Json, _1, _3](json) shouldBe Right(UserV3(Name("Decoded By Circe")))
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].