Repository: proteanhq/protean

Issue:
Establish Repository connections JIT
RepositoryFactory should prepare adapter objects and establish connections just in time so that connections are kept open only for the necessary duration. Connections are let go after processing the request.

We should initialize a unit of work at the beginning of a request, retrieve repository objects with valid current connections, process the transaction, and commit the unit of work while letting go of active connections.

Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=h1) Report
> Merging [#118](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=desc) into [master](https://codecov.io/gh/proteanhq/protean/commit/5ca86e9a0d7b3f395c7fe31b7643b034b44edfc9?src=pr&el=desc) will **increase** coverage by `0.04%`.
> The diff coverage is `87.36%`.

[![Impacted file tree graph](https://codecov.io/gh/proteanhq/protean/pull/118/graphs/tree.svg?width=650&token=0sFuFdLBOx&height=150&src=pr)](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #118      +/-   ##
==========================================
+ Coverage   82.08%   82.13%   +0.04%     
==========================================
  Files          41       45       +4     
  Lines        2088     2121      +33     
  Branches      311      311              
==========================================
+ Hits         1714     1742      +28     
- Misses        252      256       +4     
- Partials      122      123       +1
```


| [Impacted Files](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/protean/core/repository/pagination.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L3BhZ2luYXRpb24ucHk=) | `84.61% <ø> (ø)` | :arrow_up: |
| [src/protean/utils/query.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vdXRpbHMvcXVlcnkucHk=) | `82.94% <100%> (ø)` | :arrow_up: |
| [src/protean/core/field/association.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9maWVsZC9hc3NvY2lhdGlvbi5weQ==) | `88.48% <100%> (-1.67%)` | :arrow_down: |
| [src/protean/core/repository/base.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L2Jhc2UucHk=) | `100% <100%> (+9.58%)` | :arrow_up: |
| [src/protean/core/entity.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9lbnRpdHkucHk=) | `90.9% <100%> (ø)` | :arrow_up: |
| [src/protean/conf/default\_config.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29uZi9kZWZhdWx0X2NvbmZpZy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/repository/\_\_init\_\_.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L19faW5pdF9fLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/provider/base.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9wcm92aWRlci9iYXNlLnB5) | `100% <100%> (ø)` | |
| [src/protean/core/provider/\_\_init\_\_.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9wcm92aWRlci9fX2luaXRfXy5weQ==) | `68.75% <68.75%> (ø)` | |
| [src/protean/core/repository/factory.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L2ZhY3RvcnkucHk=) | `84.61% <78.26%> (+6.53%)` | :arrow_up: |
| ... and [9 more](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=footer). Last update [5ca86e9...0d30ea2](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov-io: # [Codecov](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=h1) Report
> Merging [#118](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=desc) into [master](https://codecov.io/gh/proteanhq/protean/commit/5ca86e9a0d7b3f395c7fe31b7643b034b44edfc9?src=pr&el=desc) will **increase** coverage by `0.04%`.
> The diff coverage is `87.36%`.

[![Impacted file tree graph](https://codecov.io/gh/proteanhq/protean/pull/118/graphs/tree.svg?width=650&token=0sFuFdLBOx&height=150&src=pr)](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #118      +/-   ##
==========================================
+ Coverage   82.08%   82.13%   +0.04%     
==========================================
  Files          41       45       +4     
  Lines        2088     2121      +33     
  Branches      311      311              
==========================================
+ Hits         1714     1742      +28     
- Misses        252      256       +4     
- Partials      122      123       +1
```


| [Impacted Files](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/protean/core/repository/pagination.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L3BhZ2luYXRpb24ucHk=) | `84.61% <ø> (ø)` | :arrow_up: |
| [src/protean/utils/query.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vdXRpbHMvcXVlcnkucHk=) | `82.94% <100%> (ø)` | :arrow_up: |
| [src/protean/core/field/association.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9maWVsZC9hc3NvY2lhdGlvbi5weQ==) | `88.48% <100%> (-1.67%)` | :arrow_down: |
| [src/protean/core/repository/base.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L2Jhc2UucHk=) | `100% <100%> (+9.58%)` | :arrow_up: |
| [src/protean/core/entity.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9lbnRpdHkucHk=) | `90.9% <100%> (ø)` | :arrow_up: |
| [src/protean/conf/default\_config.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29uZi9kZWZhdWx0X2NvbmZpZy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/repository/\_\_init\_\_.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L19faW5pdF9fLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/provider/base.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9wcm92aWRlci9iYXNlLnB5) | `100% <100%> (ø)` | |
| [src/protean/core/provider/\_\_init\_\_.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9wcm92aWRlci9fX2luaXRfXy5weQ==) | `68.75% <68.75%> (ø)` | |
| [src/protean/core/repository/factory.py](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L2ZhY3RvcnkucHk=) | `84.61% <78.26%> (+6.53%)` | :arrow_up: |
| ... and [9 more](https://codecov.io/gh/proteanhq/protean/pull/118/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=footer). Last update [5ca86e9...0d30ea2](https://codecov.io/gh/proteanhq/protean/pull/118?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.gitignore b/.gitignore
index 5dc65a50..32f92754 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,6 +128,7 @@ output/*/index.html
 .bootstrap
 .appveyor.token
 *.bak
+.testmondata
 
 # VS Code
 .vscode
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d07726aa..7a24acd7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,7 +8,14 @@ dev
 * Support for chained `update` and `delete` methods on Queryset
 * Support for `update_all` method for mass updates on objects
 * Support for `delete_all` method for mass deletion of objects
-* Documentation Structure refinement
+* Rename databases configuration key in Config file from ``REPOSITORIES`` to ``DATABASES``
+* Fully expand the Provider class in configuration file, to avoid assuming a Provider class name
+* Split ``Adapter`` class into ``Provider`` and ``Repository``, separating the concern of managing the database connection from performing CRUD operations on Entity data
+* Expose configured databases as ``providers`` global variable
+* Allow fetching new connection on demand of a new repository object via ``get_connection`` in ``providers``
+* Rename ``Lookup`` class to ``BaseLookup``
+* Associate Lookups with Concrete Provider classes
+* Provide option to fully bake a model class in case it needs to be decorated for a specific database, via the ``get_model`` method in concrete Provider class
 
 0.0.9 (2019-03-08)
 ------------------
diff --git a/MANIFEST.in b/MANIFEST.in
index fbf4d0e3..515298cd 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -14,6 +14,7 @@ include CHANGELOG.rst
 include CONTRIBUTING.rst
 include LICENSE
 include README.rst
+include CODE_OF_CONDUCT.rst
 
 include tox.ini .travis.yml appveyor.yml
 
diff --git a/src/protean/conf/default_config.py b/src/protean/conf/default_config.py
index 0be6a196..77070c36 100644
--- a/src/protean/conf/default_config.py
+++ b/src/protean/conf/default_config.py
@@ -18,7 +18,7 @@ SECRET_KEY = 'wR5yJVF!PVA3&bBaFK%e3#MQna%DJfyT'
 ####################
 
 # Repository connection information
-REPOSITORIES = {}
+DATABASES = {}
 
 # Default no. of records to fetch per query
 PER_PAGE = 10
diff --git a/src/protean/core/entity.py b/src/protean/core/entity.py
index fe666f75..71e4ad7e 100644
--- a/src/protean/core/entity.py
+++ b/src/protean/core/entity.py
@@ -267,13 +267,11 @@ class QuerySet:
 
     def _retrieve_model(self):
         """Retrieve model details associated with this Entity"""
-        # FIXME Move to a better placement
-
-        # Fetch Model class and connected-adapter from Repository Factory
+        # Fetch Model class and connected repository from Repository Factory
         model_cls = repo_factory.get_model(self._entity_cls_name)
-        adapter = getattr(repo_factory, self._entity_cls_name)
+        repository = getattr(repo_factory, self._entity_cls_name)
 
-        return (model_cls, adapter)
+        return (model_cls, repository)
 
     def all(self):
         """Primary method to fetch data based on filters
@@ -290,14 +288,14 @@ class QuerySet:
         # Destroy any cached results
         self._result_cache = None
 
-        # Fetch Model class and connected-adapter from Repository Factory
-        model_cls, adapter = self._retrieve_model()
+        # Fetch Model class and connected repository from Repository Factory
+        model_cls, repository = self._retrieve_model()
 
         # order_by clause must be list of keys
         order_by = model_cls.opts_.order_by if not self._order_by else self._order_by
 
         # Call the read method of the repository
-        results = adapter._filter_objects(self._criteria, self._page, self._per_page, order_by)
+        results = repository._filter_objects(self._criteria, self._page, self._per_page, order_by)
 
         # Convert the returned results to entity and return it
         entity_items = []
@@ -342,7 +340,7 @@ class QuerySet:
         Returns the number of objects matched (which may not be equal to the number of objects
             deleted if objects rows already have the new value).
         """
-        # Fetch Model class and connected-adapter from Repository Factory
+        # Fetch Model class and connected repository from Repository Factory
         deleted_item_count = 0
         try:
             items = self.all()
@@ -359,8 +357,8 @@ class QuerySet:
     def update_all(self, *args, **kwargs):
         """Updates all objects with details given if they match a set of conditions supplied.
 
-        This method forwards filters and updates directly to the adapter. It does not instantiate
-        entities and it does not trigger Entity callbacks or validations.
+        This method forwards filters and updates directly to the repository. It does not
+        instantiate entities and it does not trigger Entity callbacks or validations.
 
         Update values can be specified either as a dict, or keyword arguments.
 
@@ -368,9 +366,9 @@ class QuerySet:
             updated if objects rows already have the new value).
         """
         updated_item_count = 0
-        _, adapter = self._retrieve_model()
+        _, repository = self._retrieve_model()
         try:
-            updated_item_count = adapter._update_all_objects(self._criteria, *args, **kwargs)
+            updated_item_count = repository._update_all_objects(self._criteria, *args, **kwargs)
         except Exception as exc:
             # FIXME Log Exception
             raise
@@ -380,15 +378,15 @@ class QuerySet:
     def delete_all(self, *args, **kwargs):
         """Deletes objects that match a set of conditions supplied.
 
-        This method forwards filters directly to the adapter. It does not instantiate entities and
+        This method forwards filters directly to the repository. It does not instantiate entities and
         it does not trigger Entity callbacks or validations.
 
         Returns the number of objects matched and deleted.
         """
         deleted_item_count = 0
-        _, adapter = self._retrieve_model()
+        _, repository = self._retrieve_model()
         try:
-            deleted_item_count = adapter._delete_all_objects(self._criteria)
+            deleted_item_count = repository._delete_all_objects(self._criteria)
         except Exception as exc:
             # FIXME Log Exception
             raise
@@ -641,11 +639,11 @@ class Entity(metaclass=EntityBase):
         """Retrieve model details associated with this Entity"""
         from protean.core.repository import repo_factory  # FIXME Move to a better placement
 
-        # Fetch Model class and connected-adapter from Repository Factory
+        # Fetch Model class and connected repository from Repository Factory
         model_cls = repo_factory.get_model(cls.__name__)
-        adapter = getattr(repo_factory, cls.__name__)
+        repository = getattr(repo_factory, cls.__name__)
 
-        return (model_cls, adapter)
+        return (model_cls, repository)
 
     def clone(self):
         """Deepclone the entity, but reset state"""
@@ -726,7 +724,7 @@ class Entity(metaclass=EntityBase):
         logger.debug(
             f'Creating new `{cls.__name__}` object using data {kwargs}')
 
-        model_cls, adapter = cls._retrieve_model()
+        model_cls, repository = cls._retrieve_model()
 
         try:
             # Build the entity from the input arguments
@@ -737,7 +735,7 @@ class Entity(metaclass=EntityBase):
             entity._validate_unique()
 
             # Build the model object and create it
-            model_obj = adapter._create_object(model_cls.from_entity(entity))
+            model_obj = repository._create_object(model_cls.from_entity(entity))
 
             # Update the auto fields of the entity
             for field_name, field_obj in entity.meta_.declared_fields.items():
@@ -764,15 +762,15 @@ class Entity(metaclass=EntityBase):
         logger.debug(
             f'Saving `{self.__class__.__name__}` object')
 
-        # Fetch Model class and connected-adapter from Repository Factory
-        model_cls, adapter = self.__class__._retrieve_model()
+        # Fetch Model class and connected repository from Repository Factory
+        model_cls, repository = self.__class__._retrieve_model()
 
         try:
             # Do unique checks, update the record and return the Entity
             self._validate_unique(create=False)
 
             # Build the model object and create it
-            model_obj = adapter._create_object(model_cls.from_entity(self))
+            model_obj = repository._create_object(model_cls.from_entity(self))
 
             # Update the auto fields of the entity
             for field_name, field_obj in self.meta_.declared_fields.items():
@@ -807,8 +805,8 @@ class Entity(metaclass=EntityBase):
         """
         logger.debug(f'Updating existing `{self.__class__.__name__}` object with id {self.id}')
 
-        # Fetch Model class and connected-adapter from Repository Factory
-        model_cls, adapter = self.__class__._retrieve_model()
+        # Fetch Model class and connected repository from Repository Factory
+        model_cls, repository = self.__class__._retrieve_model()
 
         try:
             # Update entity's data attributes
@@ -816,7 +814,7 @@ class Entity(metaclass=EntityBase):
 
             # Do unique checks, update the record and return the Entity
             self._validate_unique(create=False)
-            adapter._update_object(model_cls.from_entity(self))
+            repository._update_object(model_cls.from_entity(self))
 
             # Set Entity status to saved
             self.state_.mark_saved()
@@ -828,7 +826,7 @@ class Entity(metaclass=EntityBase):
 
     def _validate_unique(self, create=True):
         """ Validate the unique constraints for the entity """
-        # Fetch Model class and connected-adapter from Repository Factory
+        # Fetch Model class and connected-repository from Repository Factory
         model_cls, _ = self.__class__._retrieve_model()
 
         # Build the filters from the unique constraints
@@ -860,13 +858,13 @@ class Entity(metaclass=EntityBase):
 
         Throws ObjectNotFoundError if the object was not found in the repository.
         """
-        # Fetch Model class and connected-adapter from Repository Factory
-        model_cls, adapter = self.__class__._retrieve_model()
+        # Fetch Model class and connected repository from Repository Factory
+        model_cls, repository = self.__class__._retrieve_model()
 
         try:
             if not self.state_.is_destroyed:
                 # Update entity's data attributes
-                adapter._delete_object(model_cls.from_entity(self))
+                repository._delete_object(model_cls.from_entity(self))
 
                 # Set Entity status to saved
                 self.state_.mark_destroyed()
diff --git a/src/protean/core/field/association.py b/src/protean/core/field/association.py
index b374c087..e078eba2 100644
--- a/src/protean/core/field/association.py
+++ b/src/protean/core/field/association.py
@@ -59,11 +59,26 @@ class Reference(FieldCacheMixin, Field):
     def __init__(self, to_cls, via=None, **kwargs):
         # FIXME ensure `via` argument is of type `str`
         super().__init__(**kwargs)
-        self.to_cls = to_cls
+        self._to_cls = to_cls
         self.via = via
 
         self.relation = ReferenceField(self)
 
+    @property
+    def to_cls(self):
+        """Property to retrieve to_cls as an entity when possible"""
+        # Checks if ``to_cls`` is a string
+        #   If it is, checks if the entity is imported and available
+        #   If it is, register the class
+        try:
+            if isinstance(self._to_cls, str):
+                self._to_cls = fetch_entity_cls_from_registry(self._to_cls)
+        except AssertionError:
+            # Preserve ``to_cls`` as a string and we will hook up the entity later
+            pass
+
+        return self._to_cls
+
     def get_attribute_name(self):
         """Return attribute name suffixed with via if defined, or `_id`"""
         return '{}_{}'.format(self.field_name, self.linked_attribute)
diff --git a/src/protean/core/provider/__init__.py b/src/protean/core/provider/__init__.py
new file mode 100644
index 00000000..176a693b
--- /dev/null
+++ b/src/protean/core/provider/__init__.py
@@ -0,0 +1,54 @@
+""" Provider classes for Database configurations and connections"""
+import importlib
+
+from protean.conf import active_config
+from protean.core.exceptions import ConfigurationError
+
+
+class Providers:
+    """Application Singleton to configure and manage database connections
+    """
+    def __init__(self):
+        self._providers = None
+
+    def _initialize_providers(self):
+        """Read config file and initialize providers"""
+        configured_providers = active_config.DATABASES
+        provider_objects = {}
+
+        if not isinstance(configured_providers, dict) or configured_providers == {}:
+            raise ConfigurationError(
+                "'DATABASES' config must be a dict and at least one "
+                "provider must be defined")
+
+        if 'default' not in configured_providers:
+            raise ConfigurationError(
+                "You must define a 'default' provider")
+
+        for provider_name, conn_info in configured_providers.items():
+            provider_full_path = conn_info['PROVIDER']
+            provider_module, provider_class = provider_full_path.rsplit('.', maxsplit=1)
+
+            provider_cls = getattr(importlib.import_module(provider_module), provider_class)
+            provider_objects[provider_name] = provider_cls(conn_info)
+
+        return provider_objects
+
+    def get_provider(self, provider_name='default'):
+        """Fetch provider with the name specified in Configuration file"""
+        try:
+            if self._providers is None:
+                self._providers = self._initialize_providers()
+            return self._providers[provider_name]
+        except KeyError:
+            raise AssertionError(f'No Provider registered with name {provider_name}')
+
+    def get_connection(self, provider_name='default'):
+        """Fetch connection from Provider"""
+        try:
+            return self._providers[provider_name].get_connection()
+        except KeyError:
+            raise AssertionError(f'No Provider registered with name {provider_name}')
+
+
+providers = Providers()
diff --git a/src/protean/core/provider/base.py b/src/protean/core/provider/base.py
new file mode 100644
index 00000000..d3a95220
--- /dev/null
+++ b/src/protean/core/provider/base.py
@@ -0,0 +1,64 @@
+"""Base class for Providers"""
+
+import uuid
+from abc import ABCMeta
+from abc import abstractmethod
+
+from protean.utils.query import RegisterLookupMixin
+
+
+class BaseProvider(RegisterLookupMixin, metaclass=ABCMeta):
+    """Provider Implementation for each database that acts as a gateway to configure the database,
+    retrieve connections and perform commits
+    """
+
+    def __init__(self, conn_info: dict):
+        """Initialize Provider with Connection/Adapter details"""
+        self.identifier = str(uuid.uuid4())
+        self.conn_info = conn_info
+
+    def _extract_lookup(self, key):
+        """Extract lookup method based on key name format"""
+        parts = key.split('__')
+        # 'exact' is the default lookup if there was no explicit comparison op in `key`
+        #   Assume there is only one `__` in the key.
+        #   FIXME Change for child attribute query support
+        op = 'exact' if len(parts) == 1 else parts[1]
+
+        # Construct and assign the lookup class as a filter criteria
+        return parts[0], self.get_lookup(op)
+
+    @abstractmethod
+    def get_session(self):
+        """Establish a new session with the database.
+
+        Typically the session factory should be created once per application. Which is then
+        held on to and passed to different transactions.
+
+        In Protean's case, the session scope and the transaction scope match. Which means that a
+        new session is created when a transaction needs to be initiated (at the beginning of
+        request handling, for example) and terminated (after committing or rolling back) at the end
+        of the process. The session will be used as a component in Unit of Work Pattern, to handle
+        transactions reliably.
+
+        Sessions are made available to requests as part of a Context Manager.
+        """
+
+    @abstractmethod
+    def get_connection(self):
+        """ Get the connection object for the repository"""
+
+    @abstractmethod
+    def close_connection(self, conn):
+        """ Close the connection object for the repository"""
+
+    @abstractmethod
+    def get_repository(self, model_cls):
+        """ Return a repository object configured with a live connection"""
+
+    def get_model(self, model_cls):
+        """ Return the fully baked model, with any additions necessary
+
+        This is a placeholder method that can be overridden in each provider
+        """
+        return model_cls
diff --git a/src/protean/core/repository/__init__.py b/src/protean/core/repository/__init__.py
index f24ea24b..e3ff154c 100644
--- a/src/protean/core/repository/__init__.py
+++ b/src/protean/core/repository/__init__.py
@@ -1,14 +1,13 @@
 """Package for defining interfaces for Repository Implementations"""
 
-from .base import BaseAdapter
-from .base import BaseConnectionHandler
-from .base import BaseModel
-from .base import Lookup
-from .base import ModelOptions
+from .base import BaseRepository
 from .factory import RepositoryFactory
 from .factory import repo_factory
+from .lookup import BaseLookup
+from .model import BaseModel
+from .model import BaseModelMeta
+from .model import ModelOptions
 from .pagination import Pagination
 
-__all__ = ('BaseAdapter', 'BaseModel', 'Lookup', 'ModelOptions',
-           'BaseConnectionHandler', 'RepositoryFactory', 'repo_factory',
-           'Pagination')
+__all__ = ('BaseRepository', 'BaseModel', 'BaseModelMeta', 'BaseLookup', 'ModelOptions',
+           'RepositoryFactory', 'repo_factory', 'Pagination')
diff --git a/src/protean/core/repository/base.py b/src/protean/core/repository/base.py
index 126fa425..5e94f763 100644
--- a/src/protean/core/repository/base.py
+++ b/src/protean/core/repository/base.py
@@ -4,41 +4,27 @@ from abc import ABCMeta
 from abc import abstractmethod
 from typing import Any
 
-from protean.core.exceptions import ConfigurationError
-from protean.utils import inflection
 from protean.utils.query import Q
-from protean.utils.query import RegisterLookupMixin
 
-from .factory import repo_factory
 from .pagination import Pagination
 
 logger = logging.getLogger('protean.repository')
 
 
-class BaseAdapter(RegisterLookupMixin, metaclass=ABCMeta):
-    """Adapter interface to interact with databases
+class BaseRepository(metaclass=ABCMeta):
+    """Repository interface to interact with databases
 
     :param conn: A connection/session to the data source of the model
     :param model_cls: The model class registered with this repository
     """
 
-    def __init__(self, conn, model_cls):
-        self.conn = conn
+    def __init__(self, provider, model_cls):
+        self.provider = provider
+        self.conn = self.provider.get_connection()
         self.model_cls = model_cls
         self.entity_cls = model_cls.opts_.entity_cls
         self.model_name = model_cls.opts_.model_name
 
-    def _extract_lookup(self, key):
-        """Extract lookup method based on key name format"""
-        parts = key.split('__')
-        # 'exact' is the default lookup if there was no explicit comparison op in `key`
-        #   Assume there is only one `__` in the key.
-        #   FIXME Change for child attribute query support
-        op = 'exact' if len(parts) == 1 else parts[1]
-
-        # Construct and assign the lookup class as a filter criteria
-        return parts[0], self.get_lookup(op)
-
     @abstractmethod
     def _filter_objects(self, criteria: Q, page: int = 1, per_page: int = 10,
                         order_by: list = ()) -> Pagination:
@@ -66,110 +52,3 @@ class BaseAdapter(RegisterLookupMixin, metaclass=ABCMeta):
     @abstractmethod
     def _delete_all_objects(self, criteria: Q):
         """Delete a Record from the Repository"""
-
-
-class ModelOptions(object):
-    """class Meta options for the :class:`BaseModel`."""
-
-    def __init__(self, meta, model_cls):
-        self.entity_cls = getattr(meta, 'entity', None)
-
-        # Import here to avoid cyclic deps
-        from protean.core.entity import Entity
-        if not self.entity_cls or not issubclass(self.entity_cls, Entity):
-            raise ConfigurationError(
-                '`entity` option must be set and be a subclass of `Entity`.')
-
-        # Get the model name to be used, if not provided default it
-        self.model_name = getattr(meta, 'model_name', None)
-        if not self.model_name:
-            self.model_name = inflection.underscore(model_cls.__name__)
-
-        # Get the database bound to this model
-        self.bind = getattr(meta, 'bind', 'default')
-
-        # Default ordering of the filter response
-        self.order_by = getattr(meta, 'order_by', ())
-
-
-class BaseModelMeta(ABCMeta):
-    """ Metaclass for the BaseModel, sets options and registers the model """
-    def __new__(mcs, name, bases, attrs):
-        klass = super().__new__(mcs, name, bases, attrs)
-
-        # Get the Meta class attribute defined for the base class
-        meta = getattr(klass, 'Meta', None)
-
-        # Load the meta class attributes for non base schemas
-        is_base = getattr(meta, 'base', False)
-        if not is_base:
-            # Set klass.opts by initializing the `options_cls` with the meta
-            klass.opts_ = klass.options_cls(meta, klass)
-
-        return klass
-
-
-class BaseModel(metaclass=BaseModelMeta):
-    """Model that defines an index/table in the repository"""
-    options_cls = ModelOptions
-    opts_ = None
-
-    class Meta(object):
-        """Options object for a Model.
-        Example usage: ::
-            class Meta:
-                entity = Dog
-        Available options:
-        - ``base``: Indicates that this is a base model so ignore the meta
-        - ``entity``: the entity associated with this model.
-        - ``model_name``: name of this model that will be used as table/index
-        names, defaults to underscore version of the class name.
-        - ``bind``: the name of the repository connection associated with this
-        model, default value is `default`.
-        - ``order_by``: default ordering of objects returned by filter queries.
-        """
-        base = True
-
-    @classmethod
-    def from_entity(cls, entity):
-        """Initialize Repository Model object from Entity object"""
-        raise NotImplementedError()
-
-    @classmethod
-    def to_entity(cls, *args, **kwargs):
-        """Convert Repository Model Object to Entity Object"""
-        raise NotImplementedError()
-
-
-class BaseConnectionHandler(metaclass=ABCMeta):
-    """ Interface to manage connections to the database """
-
-    @abstractmethod
-    def get_connection(self):
-        """ Get the connection object for the repository"""
-
-    @abstractmethod
-    def close_connection(self, conn):
-        """ Close the connection object for the repository"""
-
-
-class Lookup(metaclass=ABCMeta):
-    """Base Lookup class to implement in Adapters"""
-    lookup_name = None
-
-    def __init__(self, source, target):
-        """Source is LHS and Target is RHS of a comparsion"""
-        self.source, self.target = source, target
-
-    def process_source(self):
-        """Blank implementation; returns source"""
-        return self.source
-
-    def process_target(self):
-        """Blank implementation; returns target"""
-        return self.target
-
-    @abstractmethod
-    def as_expression(self):
-        """To be implemented in each Adapter for its Lookups"""
-        raise NotImplementedError
diff --git a/src/protean/core/repository/factory.py b/src/protean/core/repository/factory.py
index 648f13a5..2d3ab195 100644
--- a/src/protean/core/repository/factory.py
+++ b/src/protean/core/repository/factory.py
@@ -1,10 +1,9 @@
 """ Factory class for managing repository connections"""
-import importlib
 import logging
 from threading import local
 
-from protean.conf import active_config
 from protean.core.exceptions import ConfigurationError
+from protean.core.provider import providers
 
 logger = logging.getLogger('protean.repository')
 
@@ -19,98 +18,62 @@ class RepositoryFactory:
 
     def __init__(self):
         """"Initialize repository factory"""
-        self._registry = {}
+        self._provider_registry = {}
         self._entity_registry = {}
         self._model_registry = {}
+        self._fully_baked_models = {}
         self._connections = local()
-        self._repositories = None
 
-    @property
-    def repositories(self):
-        """ Return the databases configured for the application"""
-        if self._repositories is None:
-            self._repositories = active_config.REPOSITORIES
-
-        if not isinstance(self._repositories, dict) or self._repositories == {}:
-            raise ConfigurationError(
-                "'REPOSITORIES' config must be a dict and at least one "
-                "database must be defined")
-
-        if 'default' not in self._repositories:
-            raise ConfigurationError(
-                "You must define a 'default' repository")
-
-        return self._repositories
-
-    @property
-    def connections(self):
-        """ Return the registered repository connections"""
-        try:
-            return self._connections.connections
-        except AttributeError:
-            self._connections.connections = {}
-            return self._connections.connections
-
-    def register(self, model_cls, adapter_cls=None):
+    def register(self, model_cls, provider_name=None):
         """ Register the given model with the factory
         :param model_cls: class of the model to be registered
         :param adapter_cls: Optional adapter class to use if not the
         `Adapter` defined by the provider is user
         """
-        # Import here to avoid cyclic dependency
-        from .base import BaseModel
-        from .base import BaseAdapter
-
-        if not issubclass(model_cls, BaseModel):
-            raise AssertionError(
-                f'Model {model_cls} must be subclass of `BaseModel`')
-
-        if adapter_cls and not issubclass(adapter_cls, BaseAdapter):
-            raise AssertionError(
-                f'Repository {adapter_cls} must be subclass of `BaseAdapter`')
+        self._validate_model_cls(model_cls)
 
         # Register the model if it does not exist
         model_name = model_cls.__name__
         entity_name = model_cls.opts_.entity_cls.__name__
 
-        if self._registry.get(entity_name):
+        if self._provider_registry.get(entity_name):
             # This probably is an accidental re-registration of the entity
             #   and we should warn the user of a possible repository confusion
             raise ConfigurationError(
                 f'Entity {entity_name} has already been registered')
         else:
-            # Lookup the connection details for the model
-            try:
-                conn_info = self.repositories[model_cls.opts_.bind]
-            except KeyError:
-                raise ConfigurationError(
-                    f"'{model_cls.opts_.bind}' repository not found in "
-                    f"'REPOSITORIES'")
-
-            # Load the repository provider
-            provider = importlib.import_module(conn_info['PROVIDER'])
-
-            # If no connection exists then build it
-            if model_cls.opts_.bind not in self.connections:
-                conn_handler = provider.ConnectionHandler(
-                    model_cls.opts_.bind, conn_info)
-                self._connections.connections[model_cls.opts_.bind] = \
-                    conn_handler.get_connection()
-
-            # Finally register the model against the provider repository
-            adapter_cls = adapter_cls or provider.Adapter
-            self._registry[entity_name] = \
-                adapter_cls(self.connections[model_cls.opts_.bind], model_cls)
+            self._provider_registry[entity_name] = provider_name or model_cls.opts_.bind or 'default'
             self._model_registry[entity_name] = model_cls
             self._entity_registry[entity_name] = model_cls.opts_.entity_cls
             logger.debug(
-                f'Registered model {model_name} for entity {entity_name} with repository provider '
-                f'{conn_info["PROVIDER"]}.')
+                f'Registered model {model_name} for entity {entity_name} with provider'
+                f' {provider_name}.')
+
+    def _validate_model_cls(self, model_cls):
+        """Validate that Model is a valid class"""
+        # Import here to avoid cyclic dependency
+        from .model import BaseModel
+
+        if not issubclass(model_cls, BaseModel):
+            raise AssertionError(
+                f'Model {model_cls} must be subclass of `BaseModel`')
 
     def get_model(self, entity_name):
         """Retrieve Model class connected to Entity"""
+        if entity_name in self._fully_baked_models:
+            return self._fully_baked_models[entity_name]
+
         try:
-            return self._model_registry[entity_name]
+            # This will trigger ``AssertionError`` if entity is not registered
+            model_cls = self._model_registry[entity_name]
+
+            provider = self.get_provider(entity_name)
+            fully_baked_model = provider.get_model(model_cls)
+
+            # Record for future reference
+            self._fully_baked_models['entity_name'] = fully_baked_model
+
+            return fully_baked_model
         except KeyError:
             raise AssertionError(f'No Model registered for {entity_name}')
 
@@ -121,19 +84,19 @@ class RepositoryFactory:
         except KeyError:
             raise AssertionError(f'No Entity registered with name {entity_name}')
 
+    def get_provider(self, entity_name):
+        """Retrieve the provider name registered for the entity"""
+        provider_name = self._provider_registry[entity_name]
+        return providers.get_provider(provider_name)
+
     def __getattr__(self, entity_name):
         try:
-            return self._registry[entity_name]
+            provider = self.get_provider(entity_name)
+
+            # Fetch a repository object with live connection
+            return provider.get_repository(self._model_registry[entity_name])
         except KeyError:
             raise AssertionError(f'No Model registered for {entity_name}')
 
-    def close_connections(self):
-        """ Close all connections registered with the repository """
-        for conn_name, conn_obj in self.connections.items():
-            conn_info = self.repositories[conn_name]
-            provider = importlib.import_module(conn_info['PROVIDER'])
-            conn_handler = provider.ConnectionHandler(conn_name, conn_info)
-            conn_handler.close_connection(conn_obj)
-
 
 repo_factory = RepositoryFactory()
diff --git a/src/protean/core/repository/lookup.py b/src/protean/core/repository/lookup.py
new file mode 100644
index 00000000..c5477d12
--- /dev/null
+++ b/src/protean/core/repository/lookup.py
@@ -0,0 +1,25 @@
+"""Classes and Functions for Lookup methods for Query translation"""
+from abc import ABCMeta
+from abc import abstractmethod
+
+
+class BaseLookup(metaclass=ABCMeta):
+    """Base Lookup class to implement in Adapters"""
+    lookup_name = None
+
+    def __init__(self, source, target):
+        """Source is LHS and Target is RHS of a comparsion"""
+        self.source, self.target = source, target
+
+    def process_source(self):
+        """Blank implementation; returns source"""
+        return self.source
+
+    def process_target(self):
+        """Blank implementation; returns target"""
+        return self.target
+
+    @abstractmethod
+    def as_expression(self):
+        """To be implemented in each Adapter for its Lookups"""
+        raise NotImplementedError
diff --git a/src/protean/core/repository/model.py b/src/protean/core/repository/model.py
new file mode 100644
index 00000000..9340c672
--- /dev/null
+++ b/src/protean/core/repository/model.py
@@ -0,0 +1,79 @@
+""" Module containing Model related Class Definitions """
+
+from abc import ABCMeta
+
+from protean.core.exceptions import ConfigurationError
+from protean.utils import inflection
+
+
+class ModelOptions(object):
+    """class Meta options for the :class:`BaseModel`."""
+
+    def __init__(self, meta, model_cls):
+        self.entity_cls = getattr(meta, 'entity', None)
+
+        # Import here to avoid cyclic deps
+        from protean.core.entity import Entity
+        if not self.entity_cls or not issubclass(self.entity_cls, Entity):
+            raise ConfigurationError(
+                '`entity` option must be set and be a subclass of `Entity`.')
+
+        # Get the model name to be used, if not provided default it
+        self.model_name = getattr(meta, 'model_name', None)
+        if not self.model_name:
+            self.model_name = inflection.underscore(model_cls.__name__)
+
+        # Get the database bound to this model
+        self.bind = getattr(meta, 'bind', 'default')
+
+        # Default ordering of the filter response
+        self.order_by = getattr(meta, 'order_by', ())
+
+
+class BaseModelMeta(ABCMeta):
+    """ Metaclass for the BaseModel, sets options and registers the model """
+    def __new__(mcs, name, bases, attrs):
+        klass = super().__new__(mcs, name, bases, attrs)
+
+        # Get the Meta class attribute defined for the base class
+        meta = getattr(klass, 'Meta', None)
+
+        # Load the meta class attributes for non base schemas
+        is_base = getattr(meta, 'base', False)
+        if not is_base:
+            # Set klass.opts by initializing the `options_cls` with the meta
+            klass.opts_ = klass.options_cls(meta, klass)
+
+        return klass
+
+
+class BaseModel(metaclass=BaseModelMeta):
+    """Model that defines an index/table in the repository"""
+    options_cls = ModelOptions
+    opts_ = None
+
+    class Meta(object):
+        """Options object for a Model.
+        Example usage: ::
+            class Meta:
+                entity = Dog
+        Available options:
+        - ``base``: Indicates that this is a base model so ignore the meta
+        - ``entity``: the entity associated with this model.
+        - ``model_name``: name of this model that will be used as table/index
+        names, defaults to underscore version of the class name.
+        - ``bind``: the name of the repository connection associated with this
+        model, default value is `default`.
+        - ``order_by``: default ordering of objects returned by filter queries.
+        """
+        base = True
+
+    @classmethod
+    def from_entity(cls, entity):
+        """Initialize Repository Model object from Entity object"""
+        raise NotImplementedError()
+
+    @classmethod
+    def to_entity(cls, *args, **kwargs):
+        """Convert Repository Model Object to Entity Object"""
+        raise NotImplementedError()
diff --git a/src/protean/core/repository/pagination.py b/src/protean/core/repository/pagination.py
index 878bfb9a..e046df4e 100644
--- a/src/protean/core/repository/pagination.py
+++ b/src/protean/core/repository/pagination.py
@@ -1,4 +1,7 @@
-"""Utility classes and methods for Repository"""
+"""Utility classes and methods for Repository
+
+FIXME Should this be part of the domain, or be a part of Request/Response infrastructure?
+"""
 from math import ceil
 
 
diff --git a/src/protean/impl/repository/dict_repo.py b/src/protean/impl/repository/dict_repo.py
index e51cac6c..060e7e49 100644
--- a/src/protean/impl/repository/dict_repo.py
+++ b/src/protean/impl/repository/dict_repo.py
@@ -7,19 +7,49 @@ from threading import Lock
 
 from protean.core.entity import Q
 from protean.core.exceptions import ObjectNotFoundError
-from protean.core.repository import BaseAdapter
-from protean.core.repository import BaseConnectionHandler
+from protean.core.provider.base import BaseProvider
+from protean.core.repository import BaseLookup
 from protean.core.repository import BaseModel
-from protean.core.repository import Lookup
+from protean.core.repository import BaseRepository
 from protean.core.repository import Pagination
 
 # Global in-memory store of dict data. Keyed by name, to provide
 # multiple named local memory caches.
 _databases = {}
 _locks = {}
+_counters = defaultdict(count)
 
 
-class Adapter(BaseAdapter):
+class DictProvider(BaseProvider):
+    """Provider class for Dict Repositories"""
+
+    def get_session(self):
+        """Return a session object
+
+        FIXME Is it possible to simulate transactions with Dict Repo?
+        Maybe we should to be able to simulate Unit of Work transactions
+        """
+        pass
+
+    def get_connection(self):
+        """ Return the dictionary database object """
+        database = {
+            'data': _databases.setdefault(self.identifier, defaultdict(dict)),
+            'lock': _locks.setdefault(self.identifier, Lock()),
+            'counters': _counters
+        }
+        return database
+
+    def close_connection(self, conn):
+        """ Close connection does nothing on the repo """
+        pass
+
+    def get_repository(self, model_cls):
+        """ Return a repository object configured with a live connection"""
+        return DictRepository(self, model_cls)
+
+
+class DictRepository(BaseRepository):
     """ A repository for storing data in a dictionary """
 
     def _set_auto_fields(self, model_obj):
@@ -54,7 +84,7 @@ class Adapter(BaseAdapter):
         for record_key, record_value in db.items():
             match = True
 
-            stripped_key, lookup_class = self._extract_lookup(key)
+            stripped_key, lookup_class = self.provider._extract_lookup(key)
             lookup = lookup_class(record_value[stripped_key], value)
 
             if negated:
@@ -199,7 +229,7 @@ operators = {
 }
 
 
-class DefaultLookup(Lookup):
+class DefaultDictLookup(BaseLookup):
     """Base class with default implementation of expression construction"""
     def process_source(self):
         """Return source with transformations, if any"""
@@ -219,14 +249,14 @@ class DefaultLookup(Lookup):
                              self.process_target())
 
 
-@Adapter.register_lookup
-class Exact(DefaultLookup):
+@DictProvider.register_lookup
+class Exact(DefaultDictLookup):
     """Exact Match Query"""
     lookup_name = 'exact'
 
 
-@Adapter.register_lookup
-class IExact(DefaultLookup):
+@DictProvider.register_lookup
+class IExact(DefaultDictLookup):
     """Exact Case-Insensitive Match Query"""
     lookup_name = 'iexact'
 
@@ -241,8 +271,8 @@ class IExact(DefaultLookup):
         return "%s.lower()" % super().process_target()
 
 
-@Adapter.register_lookup
-class Contains(DefaultLookup):
+@DictProvider.register_lookup
+class Contains(DefaultDictLookup):
     """Exact Contains Query"""
     lookup_name = 'contains'
 
@@ -253,8 +283,8 @@ class Contains(DefaultLookup):
                              self.process_source())
 
 
-@Adapter.register_lookup
-class IContains(DefaultLookup):
+@DictProvider.register_lookup
+class IContains(DefaultDictLookup):
     """Exact Case-Insensitive Contains Query"""
     lookup_name = 'icontains'
 
@@ -275,32 +305,32 @@ class IContains(DefaultLookup):
                              self.process_source())
 
 
-@Adapter.register_lookup
-class GreaterThan(DefaultLookup):
+@DictProvider.register_lookup
+class GreaterThan(DefaultDictLookup):
     """Greater than Query"""
     lookup_name = 'gt'
 
 
-@Adapter.register_lookup
-class GreaterThanOrEqual(DefaultLookup):
+@DictProvider.register_lookup
+class GreaterThanOrEqual(DefaultDictLookup):
     """Greater than or Equal Query"""
     lookup_name = 'gte'
 
 
-@Adapter.register_lookup
-class LessThan(DefaultLookup):
+@DictProvider.register_lookup
+class LessThan(DefaultDictLookup):
     """Less than Query"""
     lookup_name = 'lt'
 
 
-@Adapter.register_lookup
-class LessThanOrEqual(DefaultLookup):
+@DictProvider.register_lookup
+class LessThanOrEqual(DefaultDictLookup):
     """Less than or Equal Query"""
     lookup_name = 'lte'
 
 
-@Adapter.register_lookup
-class In(DefaultLookup):
+@DictProvider.register_lookup
+class In(DefaultDictLookup):
     """In Query"""
     lookup_name = 'in'
 
@@ -325,24 +355,3 @@ class DictModel(BaseModel):
     def to_entity(cls, item):
         """ Convert the dictionary record to an entity """
         return cls.opts_.entity_cls(item)
-
-
-class ConnectionHandler(BaseConnectionHandler):
-    """ Handle connections to the dict repository """
-
-    def __init__(self, conn_name, conn_info):
-        self.conn_info = conn_info
-        self.conn_name = conn_name
-
-    def get_connection(self):
-        """ Return the dictionary database object """
-        database = {
-            'data': _databases.setdefault(self.conn_name, defaultdict(dict)),
-            'lock': _locks.setdefault(self.conn_name, Lock()),
-            'counters': defaultdict(count)
-        }
-        return database
-
-    def close_connection(self, conn):
-        """ Close connection does nothing on the repo """
-        pass
diff --git a/src/protean/utils/query.py b/src/protean/utils/query.py
index 89b54a12..12a04f9f 100644
--- a/src/protean/utils/query.py
+++ b/src/protean/utils/query.py
@@ -1,4 +1,4 @@
-"""Utility classes and methods for DB Adapters and Query Constructors"""
+"""Utility classes and methods for DB Adapters, Repositories and Query Constructors"""
 import copy
 import functools
 import inspect
@@ -26,11 +26,11 @@ class RegisterLookupMixin:
 
     def get_lookup(self, lookup_name):
         """Fetch Lookup by name"""
-        from protean.core.repository import Lookup
+        from protean.core.repository import BaseLookup
         lookup = self._get_lookup(lookup_name)
 
         # If unable to find Lookup, or if Lookup is the wrong class, raise Error
-        if lookup is None or (lookup is not None and not issubclass(lookup, Lookup)):
+        if lookup is None or (lookup is not None and not issubclass(lookup, BaseLookup)):
             raise NotImplementedError
 
         return lookup


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index c2c28580..f3530e4b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -15,7 +15,8 @@ def register_models():
     from protean.core.repository import repo_factory
     from tests.support.dog import (DogModel, RelatedDogModel, DogRelatedByEmailModel,
                                    HasOneDog1Model, HasOneDog2Model, HasOneDog3Model,
-                                   HasManyDog1Model, HasManyDog2Model, HasManyDog3Model)
+                                   HasManyDog1Model, HasManyDog2Model, HasManyDog3Model,
+                                   ThreadedDogModel)
     from tests.support.human import (HumanModel, HasOneHuman1Model,
                                      HasOneHuman2Model, HasOneHuman3Model,
                                      HasManyHuman1Model, HasManyHuman2Model,
@@ -37,12 +38,12 @@ def register_models():
     repo_factory.register(HasManyHuman1Model)
     repo_factory.register(HasManyHuman2Model)
     repo_factory.register(HasManyHuman3Model)
-
+    repo_factory.register(ThreadedDogModel)
 
 
 @pytest.fixture(autouse=True)
 def run_around_tests():
-    """Initialize DogModel with Dict Repo"""
+    """Cleanup Database after each test run"""
     from protean.core.repository import repo_factory
 
     # A test function will be run at this point
@@ -64,3 +65,4 @@ def run_around_tests():
     repo_factory.HasManyHuman1.delete_all()
     repo_factory.HasManyHuman2.delete_all()
     repo_factory.HasManyHuman3.delete_all()
+    repo_factory.ThreadedDog.delete_all()
diff --git a/tests/core/test_providers.py b/tests/core/test_providers.py
new file mode 100644
index 00000000..2d4db3ea
--- /dev/null
+++ b/tests/core/test_providers.py
@@ -0,0 +1,23 @@
+"""Tests for Provider Functionality"""
+from protean.core.provider import providers
+from protean.impl.repository.dict_repo import DictProvider
+
+
+class TestProviders:
+    """This class holds tests for Providers Singleton"""
+
+    def test_init(self):
+        """Test that ``providers`` object is available"""
+        assert providers is not None
+
+    def test_provider_detail(self):
+        """Test provider info loaded for tests"""
+
+        provider1 = providers.get_provider('default')
+        assert isinstance(provider1, DictProvider)
+
+    def test_provider_get_connection(self):
+        """Test ``get_connection`` method and check for connection details"""
+
+        conn = providers.get_provider('default').get_connection()
+        assert all(key in conn for key in ['data', 'lock', 'counters'])
diff --git a/tests/core/test_repository.py b/tests/core/test_repository.py
index 5f99a09b..6e74b2e2 100644
--- a/tests/core/test_repository.py
+++ b/tests/core/test_repository.py
@@ -6,7 +6,6 @@ from tests.support.dog import Dog
 from protean.core.exceptions import ObjectNotFoundError
 from protean.core.exceptions import ValidationError
 from protean.core.repository import repo_factory
-from protean.impl.repository.dict_repo import _databases
 
 
 class TestRepository:
@@ -110,20 +109,15 @@ class TestRepository:
         with pytest.raises(ObjectNotFoundError):
             Dog.get(3)
 
-    def test_close_connections(self):
-        """ Test closing all connections to the repository"""
-        assert 'default' in _databases
-        repo_factory.close_connections()
-
 
 class TestLookup:
     """This class holds tests for Lookup Class"""
 
-    from protean.core.repository import Lookup
-    from protean.impl.repository.dict_repo import Adapter
+    from protean.core.repository import BaseLookup
+    from protean.impl.repository.dict_repo import DictProvider
 
-    @Adapter.register_lookup
-    class SampleLookup(Lookup):
+    @DictProvider.register_lookup
+    class SampleLookup(BaseLookup):
         """A simple implementation of lookup class"""
         lookup_name = "sample"
 
@@ -140,6 +134,6 @@ class TestLookup:
 
     def test_registration(self):
         """Test registering a lookup to an adapter"""
-        from protean.impl.repository.dict_repo import Adapter
+        from protean.impl.repository.dict_repo import DictProvider
 
-        assert Adapter.get_lookups().get('sample') == self.SampleLookup
+        assert DictProvider.get_lookups().get('sample') == self.SampleLookup
diff --git a/tests/core/test_usecase.py b/tests/core/test_usecase.py
index 3ca3ba7c..f5526bc0 100644
--- a/tests/core/test_usecase.py
+++ b/tests/core/test_usecase.py
@@ -139,15 +139,12 @@ class TestShowUseCase:
 class TestListUseCase:
     """Tests for the generic ListUseCase Class"""
 
-    @classmethod
-    def setup_class(cls):
-        """ Setup instructions for this case """
+    def test_process_request(self):
+        """Test List UseCase's `process_request` method"""
         Dog.create(name='Murdock', owner='John', age=7)
         Dog.create(name='Jean', owner='John', age=3)
         Dog.create(name='Bart', owner='Carrie', age=6)
 
-    def test_process_request(self):
-        """Test List UseCase's `process_request` method"""
         # Build the request object and run the usecase
         request_obj = ListRequestObject.from_dict(
             Dog, dict(order_by=['age'], owner='John'))
diff --git a/tests/support/dog.py b/tests/support/dog.py
index 2e7f3ad1..57e4be3d 100644
--- a/tests/support/dog.py
+++ b/tests/support/dog.py
@@ -175,3 +175,18 @@ class HasManyDog3Model(DictModel):
         """ Meta class for model options"""
         entity = HasManyDog3
         model_name = 'has_many_dogs3'
+
+
+class ThreadedDog(Entity):
+    """This is a dummy Dog Entity class"""
+    name = field.String(required=True, max_length=50)
+    created_by = field.String(required=True, max_length=15)
+
+
+class ThreadedDogModel(DictModel):
+    """ Model for the ThreadedDog Entity"""
+
+    class Meta:
+        """ Meta class for schema options"""
+        entity = ThreadedDog
+        model_name = 'threaded_dogs'
diff --git a/tests/support/sample_config.py b/tests/support/sample_config.py
index 3fcbb956..9d81118e 100644
--- a/tests/support/sample_config.py
+++ b/tests/support/sample_config.py
@@ -8,10 +8,10 @@ SECRET_KEY = 'abcdefghijklmn'
 # Flag indicates that we are testing
 TESTING = True
 
-# Define the repositories
-REPOSITORIES = {
+# Database Configuration
+DATABASES = {
     'default': {
-        'PROVIDER': 'protean.impl.repository.dict_repo'
+        'PROVIDER': 'protean.impl.repository.dict_repo.DictProvider'
     }
 }
 
diff --git a/tests/test_context.py b/tests/test_context.py
index 9ef7b28b..b3dfa9d1 100644
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -3,14 +3,12 @@
 import threading
 import time
 
+from tests.support.dog import ThreadedDog
+
 from protean.context import context
-from protean.core import field
-from protean.core.entity import Entity
-from protean.core.repository import repo_factory
 from protean.core.tasklet import Tasklet
 from protean.core.usecase import CreateRequestObject
 from protean.core.usecase import CreateUseCase
-from protean.impl.repository.dict_repo import DictModel
 
 
 class CreateUseCase2(CreateUseCase):
@@ -23,24 +21,6 @@ class CreateUseCase2(CreateUseCase):
         return super().process_request(request_object)
 
 
-class ThreadedDog(Entity):
-    """This is a dummy Dog Entity class"""
-    name = field.String(required=True, max_length=50)
-    created_by = field.String(required=True, max_length=15)
-
-
-class ThreadedDogModel(DictModel):
-    """ Model for the ThreadedDog Entity"""
-
-    class Meta:
-        """ Meta class for schema options"""
-        entity = ThreadedDog
-        model_name = 'threaded_dogs'
-
-
-repo_factory.register(ThreadedDogModel)
-
-
 def run_create_task(thread_name, name, sleep=0):
     """Assert on Request URL"""
     if thread_name:
@@ -90,6 +70,3 @@ def test_context_with_threads():
             assert dog.created_by == 'thread_4'
         if dog.name == 'Price':
             assert dog.created_by == 'thread_5'
-
-    # Cleanup the database
-    repo_factory.ThreadedDog.delete_all()