Home

Awesome

Neuron Logo

Neuron Go Report Card GoDoc Build Status GitHub

Neuron is the golang framework that uses both code generation and interfaces to create services and microservices.

What is Neuron?

Neuron is a Golang framework that enables creation of performance critical, easily scalable services with a focus on the models - not the infrastructure.

It defines a set of structures, interfaces and functions that handles model ORM-like database access, an API server with access to its resources, authorization and authentication basics, encoding based marshaling as well as multi version access.

Design

The Aim of this project was to provide a fast way of creating performance critical services.

Model mapping

With this assumption it was necessary to define interfaces for the models and its database access, a server that handles external request and encodes using defined codecs.

In order to achieve it each model would become scanned and mapped twice - using compile-time code scan and generation and then runtime - dynamic mapping. This allows getting rid of unnecessary reflection with an ability to know the model on runtime.

Controller

Each model used by the Neuron service is stored within the controller and mapped to it's related repository.

Repository and Queries

A repository is a database access interface that allows to execute the queries within specific data storage.

The service uses query scopes which defines all required information about the resource request.

Database

In order to create and execute queries a package database provide set of functions, structures and interfaces that handles provided query.Scope reduce it to the simplest possible form and execute in related model's repository.

This package provides a way to query multiple disjoint repositories and merge its results concurrently using simple query builders.

Transactions

This approach required to implement more complicated transaction system.
Each model may use a different repository, thus an execution of the transaction needs to be divided for each repository. Each repository is also responsible only for its part of the transaction. The whole process of the transaction is being orchestrated by the database.Tx.

Install

go get github.com/neuronlabs/neuron

Related packages

Code Generation

Neuron requires the model to implement multiple interfaces. In order to make this process faster and easier a code generation tool neurogonesis - https://github.com/neuronlabs/neuron-extensions/tree/master/neurogonesis is implemented.

It contains a CLI tool that might be used as go:generate to scan golang code and implement all required interfaces for your models. See to check more details about go:generate - https://blog.golang.org/generate.

It could also generate Collections - which are the model specific non-interface database query builder and composer.

Extensions

Packages in the Neuron repository provides mostly basic structures, interfaces and functions, in order not to limit it to specific implementations.

Repository https://github.com/neuronlabs/neuron-extensions - contains implementations for specific codecs, servers, repositories and authorization/authentication services.

All of these could be used as modules that creates your neuron service faster and better.

Examples

How to get known with the Neuron better? Visit repository https://github.com/neuronlabs/neuron-examples - that shows examples on how the services could be created, for specific usages.

Quick Start

Define the models

models.go

package main

// Define go:generate generators and use it every time you change any model - using go:generate.
//go:generate neurogonesis models methods --format=goimports --single-file .
//go:generate neurogonesis collections --format=goimports  --single-file .

// User is the model that is stored on the 'main' repository.
// It is related to multiple 'Pet' models.
type User struct {
    ID      int
    Name    string
    Surname string
    Pets    []*Pet `neuron:"foreign=OwnerID"`
}

// Pet is the model related with the User.
// It is stored in the 'secondary' repository.
type Pet struct {
    ID          int
    Name        string
    Owner       *User   
    OwnerID     int
}

Create repository and server

repository.go

package main

import (
	"os"

	"github.com/neuronlabs/neuron-extensions/repository/postgres"
	"github.com/neuronlabs/neuron/log"
	"github.com/neuronlabs/neuron/repository"
)

const (
    // Environment variable that contains postgres repository access URI for the default repository.
    envDefaultPostgres  = "NEURON_DEFAULT_POSTGRES"
    // Environment variable that contains postgres repository access URI for the pets. 
    envPetsPostgres     = "NEURON_PETS_POSTGRES" 
)

// defaultRepository gets the default postgres repository configuration for the service.
func defaultRepository() *postgres.Postgres {
	uriCredentials, ok := os.LookupEnv(envDefaultPostgres)
	if !ok {
		log.Fatalf("required environment variable: %s not found", envDefaultPostgres)
	}
	return postgres.New(repository.WithURI(uriCredentials))
}

// petsRepository gets the repository configuration for the Comments model.
func petsRepository() *postgres.Postgres {
	uriCredentials, ok := os.LookupEnv(envPetsPostgres)
	if !ok {
		log.Fatalf("required environment variable %s not found", envPetsPostgres)
	}
	return postgres.New(repository.WithURI(uriCredentials))
}
Server
package main

import (
	"github.com/neuronlabs/neuron-extensions/server/http"
	"github.com/neuronlabs/neuron-extensions/server/http/api/jsonapi"
	"github.com/neuronlabs/neuron-extensions/server/http/middleware"
)

func getJsonAPI() *jsonapi.API {
	// Define new json:api specification API.
	api := jsonapi.New(
		jsonapi.WithMiddlewares(middleware.ResponseWriter(-1), middleware.LogRequest),
		// Set path prefix to '/v1/api/
		jsonapi.WithPathPrefix("/v1/api"),
		// Set the strict unmarshal flag which disallows unknown model fields in the documents.
		jsonapi.WithStrictUnmarshal(),
		// Set the models with default api handler.
		jsonapi.WithDefaultHandlerModels(&User{}, &Pet{}),		
	)
	return api
}

func getServer() *http.Server {
	// Create api based on json:api specification.
	jsonAPI := getJsonAPI()
	// Create new http server.
	s := http.New(
		// Mount json:api with the models.
		http.WithAPI(jsonAPI),
		// Set the listening port.
		http.WithPort(8080),
	)
	return s
}

Main service

main.go

package main


import (
	"context"
	"net/http"

	"github.com/neuronlabs/neuron"
	"github.com/neuronlabs/neuron/log"

	serverLogs "github.com/neuronlabs/neuron-extensions/server/http/log"		
)

func main() {
	log.NewDefault()
	n := neuron.New(
		// Set the http server with json:api into service server.
		neuron.Server(getServer()),
		// Define the default repository name for all models without repository name specified.
		neuron.DefaultRepository(defaultRepository()),
		// Set the custom repository for the comments model.
		neuron.RepositoryModels(petsRepository(), &Pet{}),
		// Set the models in the service - Neuron_Models are defined by the go:generate models
		neuron.Models(Neuron_Models...),
		// Migrate models into service - this would create database definitions for the provided models.
		neuron.MigrateModels(Neuron_Models...),
		// Initialize model collections - Neuron_Collections 
		neuron.Collections(Neuron_Collections...),
	)
	log.SetLevel(log.LevelDebug3)
	serverLogs.SetLevel(log.LevelDebug3)

	ctx := context.Background()
    // Initialize the service
	if err := n.Initialize(ctx); err != nil {
		log.Fatalf("Initialize failed: %v", err)
	}

	// List all endpoints defined in the server and print their paths.
	for _, endpoint := range n.Server.GetEndpoints() {
		log.Infof("Endpoint [%s] %s", endpoint.HTTPMethod, endpoint.Path)
	}
	if err := n.Run(ctx); err != nil && err != http.ErrServerClosed {
		log.Errorf("Running neuron service failed: %s", err)
	}
}

Packages

The neuron is composed of the following packages:

Docs