Add an intermediate SchemaLoader object that allows for the manual adding of additional schemas.
If multiple schemas try to identify as the same $id an error is returned
diff --git a/jsonLoader.go b/jsonLoader.go
index 5557025..cbe4664 100644
--- a/jsonLoader.go
+++ b/jsonLoader.go
@@ -107,7 +107,7 @@
 }
 
 // NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
-func NewReferenceLoader(source string) *jsonReferenceLoader {
+func NewReferenceLoader(source string) JSONLoader {
 	return &jsonReferenceLoader{
 		fs:     osFS,
 		source: source,
@@ -115,7 +115,7 @@
 }
 
 // NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
-func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) *jsonReferenceLoader {
+func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader {
 	return &jsonReferenceLoader{
 		fs:     fs,
 		source: source,
@@ -219,7 +219,7 @@
 	return &DefaultJSONLoaderFactory{}
 }
 
-func NewStringLoader(source string) *jsonStringLoader {
+func NewStringLoader(source string) JSONLoader {
 	return &jsonStringLoader{source: source}
 }
 
@@ -247,7 +247,7 @@
 	return &DefaultJSONLoaderFactory{}
 }
 
-func NewBytesLoader(source []byte) *jsonBytesLoader {
+func NewBytesLoader(source []byte) JSONLoader {
 	return &jsonBytesLoader{source: source}
 }
 
@@ -274,7 +274,7 @@
 	return &DefaultJSONLoaderFactory{}
 }
 
-func NewGoLoader(source interface{}) *jsonGoLoader {
+func NewGoLoader(source interface{}) JSONLoader {
 	return &jsonGoLoader{source: source}
 }
 
@@ -295,12 +295,12 @@
 	buf *bytes.Buffer
 }
 
-func NewReaderLoader(source io.Reader) (*jsonIOLoader, io.Reader) {
+func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) {
 	buf := &bytes.Buffer{}
 	return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
 }
 
-func NewWriterLoader(source io.Writer) (*jsonIOLoader, io.Writer) {
+func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) {
 	buf := &bytes.Buffer{}
 	return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
 }
diff --git a/schema.go b/schema.go
index 0746a59..4ae3c62 100644
--- a/schema.go
+++ b/schema.go
@@ -46,41 +46,7 @@
 )
 
 func NewSchema(l JSONLoader) (*Schema, error) {
-	ref, err := l.JsonReference()
-	if err != nil {
-		return nil, err
-	}
-
-	d := Schema{}
-	d.pool = newSchemaPool(l.LoaderFactory())
-	d.documentReference = ref
-	d.referencePool = newSchemaReferencePool()
-
-	var spd *schemaPoolDocument
-	var doc interface{}
-	if ref.String() != "" {
-		// Get document from schema pool
-		spd, err = d.pool.GetDocument(d.documentReference)
-		if err != nil {
-			return nil, err
-		}
-		doc = spd.Document
-	} else {
-		// Load JSON directly
-		doc, err = l.LoadJSON()
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	d.pool.ParseReferences(doc, ref)
-
-	err = d.parse(doc)
-	if err != nil {
-		return nil, err
-	}
-
-	return &d, nil
+	return NewSchemaLoader().Compile(l)
 }
 
 type Schema struct {
diff --git a/schemaLoader.go b/schemaLoader.go
new file mode 100644
index 0000000..09d3a37
--- /dev/null
+++ b/schemaLoader.go
@@ -0,0 +1,102 @@
+package gojsonschema
+
+import (
+	"github.com/xeipuuv/gojsonreference"
+)
+
+type SchemaLoader struct {
+	pool *schemaPool
+}
+
+func NewSchemaLoader() *SchemaLoader {
+
+	ps := &SchemaLoader{
+		pool: &schemaPool{
+			schemaPoolDocuments: make(map[string]*schemaPoolDocument),
+		},
+	}
+
+	return ps
+}
+
+// AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require
+// an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema
+func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error {
+	emptyRef, _ := gojsonreference.NewJsonReference("")
+
+	for _, loader := range loaders {
+		doc, err := loader.LoadJSON()
+		if err != nil {
+			return err
+		}
+		// Directly use the Recursive function, so that it get only added to the schema pool by $id
+		// and not by the ref of the document as it's empty
+		if err = sl.pool.parseReferencesRecursive(doc, emptyRef); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+//AddSchema adds a schema under the provided URL to the schema cache
+func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error {
+
+	ref, err := gojsonreference.NewJsonReference(url)
+
+	if err != nil {
+		return err
+	}
+
+	doc, err := loader.LoadJSON()
+
+	if err != nil {
+		return err
+	}
+
+	return sl.pool.ParseReferences(doc, ref)
+}
+
+func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) {
+
+	ref, err := rootSchema.JsonReference()
+
+	if err != nil {
+		return nil, err
+	}
+
+	d := Schema{}
+	d.pool = sl.pool
+	d.pool.jsonLoaderFactory = rootSchema.LoaderFactory()
+	d.documentReference = ref
+	d.referencePool = newSchemaReferencePool()
+
+	var doc interface{}
+	if ref.String() != "" {
+		// Get document from schema pool
+		spd, err := d.pool.GetDocument(d.documentReference)
+		if err != nil {
+			return nil, err
+		}
+		doc = spd.Document
+	} else {
+		// Load JSON directly
+		doc, err = rootSchema.LoadJSON()
+		if err != nil {
+			return nil, err
+		}
+		// References need only be parsed if loading JSON directly
+		//  as pool.GetDocument already does this for us if loading by reference
+		err = d.pool.ParseReferences(doc, ref)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	err = d.parse(doc)
+	if err != nil {
+		return nil, err
+	}
+
+	return &d, nil
+}
diff --git a/schemaLoader_test.go b/schemaLoader_test.go
new file mode 100644
index 0000000..3ba3a8b
--- /dev/null
+++ b/schemaLoader_test.go
@@ -0,0 +1,60 @@
+package gojsonschema
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSchemaLoaderWithReferenceToAddedSchema(t *testing.T) {
+	ps := NewSchemaLoader()
+	err := ps.AddSchemas(NewStringLoader(`{
+		"$id" : "http://localhost:1234/test1.json",
+		"type" : "integer"
+		}`))
+
+	assert.Nil(t, err)
+	schema, err := ps.Compile(NewReferenceLoader("http://localhost:1234/test1.json"))
+	assert.Nil(t, err)
+	result, err := schema.Validate(NewStringLoader(`"hello"`))
+	assert.Nil(t, err)
+	if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" {
+		t.Errorf("Expected invalid type erorr, instead got %v", result.Errors())
+	}
+}
+
+func TestCrossReference(t *testing.T) {
+	schema1 := NewStringLoader(`{
+		"$ref" : "http://localhost:1234/test3.json",
+		"definitions" : {
+			"foo" : {
+				"type" : "integer"
+			}
+		}
+	}`)
+	schema2 := NewStringLoader(`{
+		"$ref" : "http://localhost:1234/test2.json#/definitions/foo"
+	}`)
+
+	ps := NewSchemaLoader()
+	err := ps.AddSchema("http://localhost:1234/test2.json", schema1)
+	assert.Nil(t, err)
+	err = ps.AddSchema("http://localhost:1234/test3.json", schema2)
+	assert.Nil(t, err)
+	schema, err := ps.Compile(NewStringLoader(`{"$ref" : "http://localhost:1234/test2.json"}`))
+	assert.Nil(t, err)
+	result, err := schema.Validate(NewStringLoader(`"hello"`))
+	assert.Nil(t, err)
+	if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" {
+		t.Errorf("Expected invalid type erorr, instead got %v", result.Errors())
+	}
+}
+
+// Multiple schemas identifying under the same $id should throw an error
+func TestDoubleIDRefernce(t *testing.T) {
+	ps := NewSchemaLoader()
+	err := ps.AddSchema("http://localhost:1234/test4.json", NewStringLoader("{}"))
+	assert.Nil(t, err)
+	err = ps.AddSchemas(NewStringLoader(`{ "$id" : "http://localhost:1234/test4.json"}`))
+	assert.NotNil(t, err)
+}
diff --git a/schemaPool.go b/schemaPool.go
index 65c91e4..c3de8e5 100644
--- a/schemaPool.go
+++ b/schemaPool.go
@@ -28,6 +28,7 @@
 
 import (
 	"errors"
+	"fmt"
 	"reflect"
 
 	"github.com/xeipuuv/gojsonreference"
@@ -51,13 +52,17 @@
 	return p
 }
 
-func (p *schemaPool) ParseReferences(document interface{}, ref gojsonreference.JsonReference) {
+func (p *schemaPool) ParseReferences(document interface{}, ref gojsonreference.JsonReference) error {
 	// Only the root document should be added to the schema pool
+	if _, ok := p.schemaPoolDocuments[ref.String()]; ok {
+		return fmt.Errorf("Reference already exists: \"%s\"", ref.String())
+	}
+	err := p.parseReferencesRecursive(document, ref)
 	p.schemaPoolDocuments[ref.String()] = &schemaPoolDocument{Document: document}
-	p.parseReferencesRecursive(document, ref)
+	return err
 }
 
-func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference) {
+func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference) error {
 	// parseReferencesRecursive parses a JSON document and resolves all $id and $ref references.
 	// For $ref references it takes into account the $id scope it is in and replaces
 	// the reference by the absolute resolved reference
@@ -81,6 +86,9 @@
 			if err == nil {
 				localRef, err = ref.Inherits(jsonReference)
 				if err == nil {
+					if _, ok := p.schemaPoolDocuments[localRef.String()]; ok {
+						return fmt.Errorf("Reference already exists: \"%s\"", localRef.String())
+					}
 					p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document}
 				}
 			}
@@ -114,6 +122,7 @@
 			}
 		}
 	}
+	return nil
 }
 
 func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) {