Last active
August 29, 2015 14:04
-
-
Save tjstebbing/d7cfe81d12f1dde7b868 to your computer and use it in GitHub Desktop.
Concepts for HT
This file contains 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
# Hudson Taylor | |
Hudson Taylor (HT) is a library for building software systems with a Service | |
Oriented Architecture. It comprises of server library for providing services | |
with well documented and defined APIs, and a client library for calling | |
services. All service APIs have Schemas that both document expectations as | |
well as validate and pre-process incoming data. | |
HT can be used within a single process to logically partition services from | |
the beginning. This means that your project has clean internal interfaces and | |
when it comes time to scale out, you can break-out services and replicate them | |
horizontally. | |
## Server quick-start: | |
### index.js | |
```javascript | |
var ht = require("hudson-taylor"); | |
var myServiceSetup = require("./echo").setup; // Import your service setup. | |
var s = ht.Service(); | |
s.add("myEchoService", myServiceSetup); | |
s.listenHTTP({port : 7001}); // Start the service listening via HTTP. | |
``` | |
### echo.js | |
```javascript | |
exports.setup = function(s, readyCallback) { | |
/* service setup functions take a service object and a callback, You must | |
* call the callback as soon as your service is ready to receive jobs. | |
*/ | |
readyCallback(); // No async setup, callback immediately. | |
//Implement an echo API schema with documentation: | |
var echoSchema = { | |
_doc : "Echo API, returns string input that you send to it.", | |
input : s.String({ | |
maxLength : 200, | |
_doc : "Input string to be returned" }), | |
} | |
s.on("echo", schema, function(data, callback) { | |
callback(null, data.input); // Data returned must be encodable as JSON | |
}); | |
//Other API handlers here.. | |
} | |
``` | |
## Client quick-start: | |
This connects to our echo service via HTTP and logs a returned string. | |
```javascript | |
var s = ht.Services(); | |
s.connect("myEchoService", ht.HTTPClient("http://auth.localhost", 7001)); | |
s.remote("myEchoService", "echo", {input : "Hello World!", function(err, res) { | |
if(!err) return console.error(err); | |
console.log(res); | |
}); | |
``` | |
# The details: | |
## Methods of communicating between services: | |
One of the nice things about HT is that you can connect to a service via several | |
different methods without changing your service code in the slightest. | |
This can make deployment very flexible, here are some of the communication | |
methods: | |
''' server method / client method ''' | |
* listenHTTP / HTTPClient: uses JSON-RPC over HTTP to communicate. | |
* listenHTTP / HTTPRoundRobinClient: uses JSON-RPC over HTTP to communicate | |
with multiple instances of a service. | |
* listenTCP / TCPClient: uses JSON-RPC over TCP to communicate. | |
* N/A / LocalServiceClient: Host a service natively in process and use JSON | |
and local function calls to communicate. This is very useful for development, | |
testing environments or small deployments. | |
Custom server and client channels can be implemented easily. | |
# Building Services | |
## service setup: | |
The easiest way to get started writing services is to export a setup function for each service, and then | |
pass those to ht.Services().add(). Your service setup function will register one or more handlers using | |
service.on() (see below). The signature for a service setup function is: | |
```javascript | |
function(<service>, readyCallback, /* any extra arguments passed to ht.Services.add() */) | |
``` | |
The extra arguments to add() allow you to pass in other services, database connections, logging etc that you | |
can use within your signal handlers, for instance here is a contrived authentication API that calls out to a | |
'session' service to check an auth token before continuing with regular authentication: | |
### index.js | |
```javascript | |
authSetup = require("./auth").setup; | |
var db, logger, services = null; | |
/* Do some db setup, configure client services, then: */ | |
services.add("auth", authSetup, db, logger, services); | |
``` | |
And then in your setup: | |
```javascript | |
exports.setup = function(s, ready, db, logger, otherService) { | |
s.on("login", <schema>, function(data, callback) { | |
// Check if we've got a valid oAuth token | |
services.remote("session", "checkToken", {token : data.token}, function(err, sessionCheck) { | |
if(err) return callback(err); | |
if(!sessionCheck.authenticated) { | |
//user needs to authenticate again | |
db.users.find({id : data.userID}, function(err, user) { | |
//do some checks | |
callback(authenticated); | |
}); | |
}); | |
}); | |
}) | |
## service.on("signal", <schema>, handler(data, callback), /* optional */ validationErrorHandler(err, callback)) | |
## Schemas | |
```javascript | |
var schema = s.Obj({ | |
_doc : "Echo API, returns string input that you send to it.", | |
input : s.String({ | |
maxLength : 200, | |
_doc : "Input string to be returned" }), | |
}) | |
``` | |
Each schema type is an object with a 'validate' method that will attempt to validate the object | |
and return the new data, or alternatively raise an error. These can be used to do type conversion | |
if required. They also have a 'document' method which is called when generating API documentation. | |
There are several built-in schema types and you can easily implement your own. This can be useful | |
for doing automatic coversion of application specific types, for instance converting an 'id' string | |
into a mongoDB ObjectID. | |
## Built-in Schema types: | |
### s.Int | |
### s.Float | |
### s.Str | |
### s.Array | |
### s.Obj | |
props : { } | |
### s.Bool | |
### s.Time | |
### s.DateTime | |
### s.and | |
### s.or | |
## Common attributes: | |
### _doc <markdown string> | |
### _docFile <markdown file> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment