Giter Club home page Giter Club logo

hubi's Introduction

Hubi Build Status JavaScript Style Guide Coverage Status GitHub licenseBCH compliance

Hubi is like a database migration tool but for ubiquitous language i.e. it reads some of your versioned files to write code, data schema and documents.

how to use hubi

When to use hubi

Use it in one or more of the following situations:

  1. If you want to have a documentated language for your domain, especially when it needs to be consulted by non-technical stakeholders.

  2. When working on a project spread into different repositories e.g. back- and front-ends, so changes in one of them needs to be repeatead in the other ones in order to make the end-result work.

⚠️ Hubi does not connect, or tell you how to connect, the project's repositories.

  1. Your repositories are modeled in a way where it is possible to share domain knowledge across them interchangeably.

  2. You want to reuse a repository's slice of the domain (the way you've coded the model) in another repository from different projects. This is the least advisable scenario, as each repository generally has a slightly different perspective on how the domain looks like.

Basic Use

Declare how your domain should look like in a YAML file, refered to as domain file, and then execute hubi with the options provided by its API ⭐.

Each one of this files is responsible for a piece of the domain. Its rules can be found at hubi's domain file guide 📗.

Installation

As hubi is a development tool, it is recommended that each project have it installed by using the developer flag.

$ npm i hubi --save-dev

or

$ yarn add hubi --dev

Example

In an imaginary project, the stakeholders have decided that it is necessary to show the user's birthday in their profile page. So the file relative to the user concept is changed:

 # Domain file @ project/src/domain/user.yml
 name: User
 description: A person who has an account
 attributes:
   - name: name
     description: How to address the person
     required: true
   - name: eyes
     description: The color of the user's eyes
+    deprecated:
+    - message:
+        We've discovered that this is a useless information for us.
+        Will be deprecated soon
+    - error: false
-    required: true
+    required: false
+  - name: birthday
+    comment: This field was added later
+    type: date
+    required: false

With one command, it is possible to spread this change to the code, data schema and documentation. You decide which translations hubi writes:

$ npm run my-custom-hubi-script
> hubi save --same-folder --translator joi & hubi save --output documents --translator site

In the example below, a Joi schema and an entry into the project's site were changed to reflect that change.

Contributing

Take a look at our 📗 contributing guide to improve hubi.

How does hubi relates to domain driven design (DDD)?

The only relation is that hubi is based on the concept of “ubiquitous language”, something Eric Evans presents in his book about DDD.

I don't know if he come up with this idea or only used it as his book's initial seed. A language-to-rule-them-all is a communication tool that simply states that we should encode domain knowledge (terms, phrases, etc) into the codebase in order to bridge the gap between developers and domain experts.

But you can still use ubiquitous language regardless of DDD because it stands on its own. So, even if you don't know/use it, you can still use hubi to reap the benefits of speaking a single language - at the same time you document your domain as code.

hubi's People

Contributors

dependabot[bot] avatar mvcds avatar semantic-release-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

hubi's Issues

Get rid of DomainFile entity

Desciption

DomainFile is an entity which may have its data to the token and therefore be removed as sourc file i.e. it will still exist as an ubiquitous language's concept (the YAML file for DomainFile is not removed)

How does it would enhance hubi?

  1. One source file less to worry about
  2. Removes a "useless" indirection

Enums

Values could be limited to a certain amount of enumerated values

Keep in mind that in some languages, those values are like tokens themselves, while in others they are just like strings

And that some languages associate their "keys" to specific numbers in an orderly manner and others allow you to freely associate values


It is possible to make flags using enums

Improve output control

Some configurations require centralizing files into a single folder but others may want to allow a better control where to save files.

For instance, I personally would like to output some source files in the same folder as their domain file.

Configuration files

Allow a file where users may tweak how hubi behaves in order to attend their needs.

Split domain and use cases

There should be a clear layer separation between what's part of Hubi's domain and what is part of its use cases

Create a CLI client

Migrate the current CLI to the correct layer (according to Clean Architecture)

Create adapters

In order to have more than one client, Hubi would benefit to have an adapter layer between the external world and its use cases

Create a web client

This could be React client where a server some documents are read in order to create the client's domain model

Allow Attribute Extensions

Some attributes may have their own configuration, for instance, strings can have min and max length but booleans cannot.

What about adding the ones you need the most?

Add Joi Translator

The first library Translator will allow who uses joi to create ubiquitous source files.

Any

  • any.allow(value)
  • any.valid(value) - aliases: only, equal
  • any.invalid(value) - aliases: disallow, not
  • any.required() - aliases: exist
  • any.optional()
  • any.forbidden()
  • any.strip()
  • any.description(desc)
  • any.notes(notes)
  • any.tags(tags)
  • any.meta(meta)
  • any.example(value)
  • any.unit(name)
  • any.options(options)
  • any.strict(isStrict)
  • any.default([value, [description]])
  • any.concat(schema)
  • any.when(condition, options)
  • any.label(name)
  • any.raw(isRaw)
  • any.empty(schema)
  • any.error(err)

Array

  • array.sparse([enabled])
  • array.single([enabled])
  • array.items(type)
  • array.ordered(type)
  • array.min(limit)
  • array.max(limit)
  • array.length(limit)
  • array.unique([comparator])

Boolean

  • boolean.truthy(value)
  • boolean.falsy(value)
  • boolean.insensitive([enabled])

Binay

  • binary.encoding(encoding)
  • binary.min(limit)
  • binary.max(limit)
  • binary.length(limit)

Date

  • date.min(date)
  • date.max(date)
  • date.iso()
  • date.timestamp([type])

Func

  • func.arity(n)
  • func.minArity(n)
  • func.maxArity(n)
  • func.class()
  • func.ref()

Number

  • number.min(limit)
  • number.max(limit)
  • number.greater(limit)
  • number.less(limit)
  • number.integer()
  • number.precision(limit)
  • number.multiple(base)
  • number.positive()
  • number.negative()

Object

  • object.keys([schema])
  • Joi.object([schema]) notation
  • Joi.object().keys([schema]) notation
  • object.min(limit)
  • object.max(limit)
  • object.length(limit)
  • object.pattern(regex, schema)
  • object.and(peers)
  • object.nand(peers)
  • object.or(peers)
  • object.xor(peers)
  • object.with(key, peers)
  • object.without(key, peers)
  • object.rename(from, to, [options])
  • object.assert(ref, schema, [message])
  • object.unknown([allow])
  • object.type(constructor, [name])
  • object.schema()
  • object.requiredKeys(children)
  • object.optionalKeys(children)
  • object.forbiddenKeys(children)

String

  • string.insensitive()
  • string.min(limit, [encoding])
  • string.max(limit, [encoding])
  • string.truncate([enabled])
  • string.creditCard()
  • string.length(limit, [encoding])
  • string.regex(pattern, [name | options])
  • string.replace(pattern, replacement)
  • string.alphanum()
  • string.token()
  • string.email([options])
  • string.ip([options])
  • string.uri([options])
  • string.guid() - aliases: uuid
  • string.hex()
  • string.base64([options])
  • string.hostname()
  • string.normalize([form])
  • string.lowercase()
  • string.uppercase()
  • string.trim()
  • string.isoDate()

Alternatives

  • alternatives.try(schemas)
  • alternatives.when(condition, options)

Lazy

  • lazy(fn) - inherits from Any

Read Sample

Allow scanning a source file in order to output a domain file

Watch flag

Add a watch flag to rerun hubi on file changes

How does it would enhance hubi?

Users do not need to pollute their packages with a watcher solution

Decorators do not follow the domain file guide

The domain file guide states the following about decorators

Each attribute type may have some decorators associated with it, enhancing how they are translated.

But the only decorator in place is range, because decorators were a late concept.

This issue aims to include of, return, arguments and any other that may appear, as decorators.

For that, decorators should be considerated part of the domain language.

Dogfood

Use hubi to generate hubi

How does it would enhance hubi?

It is the best proof of concept ever

Export SCHEMA's default

Having default values exported would allow other code to reference them, making easy to not repeat writing code

Fix joi numbers

Joi uses the same type of number for both integer and floats, but hubi is generating two

Environment

All

How to reproduce

Create a domain file of type integer or type float

Expectations

Expected Behaviour

It was supposed to yield Joi.number() flagged with "integer" when appropriated

Actual Behaviour

Generates Joi.integer() and Joi.float() when both do not exist

Externalize Decorators

Allow hubi to use decorators (from #106) which are not in this same repository.

For instance, configuring a required string to have a max length of 30 digits in a lot of domain files is boring, so allowing users to create a AlphanumericWithFixedLengthTo30 just for that project would avoid a lot of repeatition.

Useful link: nodejs.org/en/blog/npm/peer-dependencies

Contexts

A hubi context means that tokens and attributes may yield different generated source files.

There are 4 kinds of contextual changes I can think of: add, delete, change and referential.

Example

Givin a token User, I can think in the following contexts, which exist in different repositories of an App

Back-end

  1. Domain model: Uses Joi Translator to yield user_id, user_name, user_name and user_registred_at attributes
  2. Sequelize model: Uses Sequelize Translator to generate the same attributes as 1
  3. GraphQL type: Uses GrapqQL Translator but renames the tokens attributes in order to not expose my internal database schema, by not using "user_" as a prefix and renaming the token to Custumer

Front-end

  1. Domain model: Uses Joi Translator to yield the same attributes as 3 but also rename the token to Custumer
  2. React component: Translates exactly as the previous point but using PropTypes Translator

Achievement Service

  1. Domain model: Uses Joi Translator to yield user_id attribute, as it is all it needs to send achievements

Why this issue is important

At the moment a translator yields exactly what is described by a domain file.

Command to start

Make an interactive hubi start (or init) which tries to help you installing translators for you

Desciption

It should be done after externalizing translators #25 as a complement to it. Or maybe as its first version.

How does it would enhance hubi?

Helps "lazy" people to find some translators and start projects

Duplicated attributes

Sometimes we duplicate duplicate token's attributes.

Maybe give users an option about how to deal with duplication: do they consider it an error or a warning?

Tokens' name resolution

Sometimes we duplicate tokens themselves - by repeating their names

Maybe give users an option about how to deal with duplication: do they consider it an error or a warning?

Ubiquitous Inclusion

Include generated translations on their files, instead of creating a file apart.

Note or Message

Desciption

Add a way to annotate over tokens and attributes

How does it would enhance hubi?

Currently, deprecation may be used to signalize something which is not a good fit for description.

Strings cannot be null even not required @4.9.0

Setting a string as non-required don't allow to use nulls as its value

Enviroment

How to reproduce

name: Foo
attributes:
  - name: optionalString
    required: false
    default: null

Expectations

Expected Behaviour

let success = false
try {
	Joi.attempt({ optionalString: null }, Foo.SCHEMA);
	success = true
} catch(e) {
}

assert.ok(success, 'optionalString should be possible to be set to null')

In the code above the assert.ok should not fail

Actual Behaviour

It fails with the message "[1] "optionalString" must be a string"

C# Translator

The first non-js Translator I could come up with (as it was my previous fluent language).

I thought about generating C# partial classes where hubi's half is the one with the getters/setters variables the class public allows people to poke with.

Nested Array

Arrays having arrays that have arrays filled with arrays should be okayish

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.