diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000000000000000000000000000000000000..246838cbda2c246a9ac3124fe28aceb8e751250a
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,2 @@
+[flake8]
+exclude = build/*,venv/*,doc/source/*,tests/utils.py,braindump/*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a5b8250f00133f692cf2b43e6617e9d7acc6b124
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,19 @@
+language: c
+cache:
+  directories:
+      - "$HOME/.cache"
+      - "$HOME/.ccache"
+before_install:
+    - ccache -s
+    - export PATH=/usr/lib/ccache:${PATH}
+    - wget https://package-data.enthought.com/edm/rh5_x86_64/1.4/edm_1.4.1_linux_x86_64.sh && bash ./edm_1.4.1_linux_x86_64.sh -b -p $HOME
+    - export PATH=${HOME}/edm/bin:${PATH}
+    - edm environments create --version 3.5 force 
+    - . $HOME/.edm/envs/force/bin/activate 
+    - cat requirements/edm_requirements.txt | grep -v "^#" | while read line; do edm install -y `echo $line | awk '{print $1"=="$2}'`; done 
+install:
+    - pip install -r requirements/requirements.txt
+    - pip install -e .
+script:
+    - pip install -r requirements/dev_requirements.txt
+    - flake8 .
diff --git a/README.md b/README.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..68e6a408d871425da72d2320d1bf8ee666f18e57 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,8 @@
+FORCE BDSS
+----------
+
+This repository contains the implementation of the Business Decision System. It is implemented
+under the FORCE project (Horizon 2020/NMBP-23-2016/721027).
+
+
+
diff --git a/braindump/main.py b/braindump/main.py
index cbac2019f5f7dbf3f3277a66506c251f56e877f4..a61d6a00c4e4b9534c8375c5ecdf4fa738d95930 100644
--- a/braindump/main.py
+++ b/braindump/main.py
@@ -1,4 +1,4 @@
-from force import *
+from force_bdss import *
 
 wf=Workflow()
 wf.set_mco(Dakota())
diff --git a/examples/test_workflow.json b/examples/test_workflow.json
new file mode 100644
index 0000000000000000000000000000000000000000..1bbd3661e3c989d1f278022b08b73b7c639b0c41
--- /dev/null
+++ b/examples/test_workflow.json
@@ -0,0 +1,9 @@
+{
+    "multi_criteria_optimization": {
+        "name": "basic"
+    },
+    "key_performance_indicators": [
+        "viscosity", "price"
+    ]
+}
+
diff --git a/force/__init__.py b/force_bdss/__init__.py
similarity index 100%
rename from force/__init__.py
rename to force_bdss/__init__.py
diff --git a/force_bdss/bdss_application.py b/force_bdss/bdss_application.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6abb68250d434c03d1f88ff68fae5b9b6c91141
--- /dev/null
+++ b/force_bdss/bdss_application.py
@@ -0,0 +1,27 @@
+import json
+
+from envisage.api import Application
+from traits.api import Unicode, Bool, Instance
+
+from force_bdss.workspecs.workflow import Workflow
+
+
+class BDSSApplication(Application):
+    """Main application for the BDSS.
+    """
+    id = "force_bdss.bdss_application"
+
+    #: The path of the workflow file to open
+    workflow_filepath = Unicode()
+
+    #: Deserialized content of the workflow file.
+    workflow = Instance(Workflow)
+
+    #: This flags signals to the application not to execute and orchestrate
+    #: the MCO, but instead to perform a single evaluation under the
+    #: coordination of the MCO itself. See design notes for more details.
+    evaluate = Bool()
+
+    def _workflow_default(self):
+        with open(self.workflow_filepath) as f:
+            return Workflow.from_json(json.load(f))
diff --git a/force_bdss/cli/force_bdss.py b/force_bdss/cli/force_bdss.py
new file mode 100644
index 0000000000000000000000000000000000000000..37541e66f1d1132406392ab73c66da41d0779870
--- /dev/null
+++ b/force_bdss/cli/force_bdss.py
@@ -0,0 +1,30 @@
+import click
+from envisage.core_plugin import CorePlugin
+
+from force_bdss.bdss_application import BDSSApplication
+from force_bdss.core_mco_driver import CoreMCODriver
+from force_bdss.kpi.key_performance_calculators_plugin import \
+    KeyPerformanceCalculatorsPlugin
+from force_bdss.mco.multi_criteria_optimizers_plugin import \
+    MultiCriteriaOptimizersPlugin
+
+
+@click.command()
+@click.option("--evaluate", is_flag=True)
+@click.argument('workflow_filepath', type=click.Path(exists=True))
+def run(evaluate, workflow_filepath):
+
+    plugins = [
+        CorePlugin(),
+        CoreMCODriver(),
+        KeyPerformanceCalculatorsPlugin(),
+        MultiCriteriaOptimizersPlugin(),
+    ]
+
+    application = BDSSApplication(
+        plugins=plugins,
+        evaluate=evaluate,
+        workflow_filepath=workflow_filepath
+    )
+
+    application.run()
diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0409df1f556745814ce739d2a7c8d1c1d0307c7
--- /dev/null
+++ b/force_bdss/core_mco_driver.py
@@ -0,0 +1,65 @@
+from envisage.extension_point import ExtensionPoint
+from envisage.plugin import Plugin
+from traits.has_traits import on_trait_change
+from traits.trait_types import List
+
+from force_bdss.kpi.i_key_performance_calculator import (
+    IKeyPerformanceCalculator)
+from force_bdss.mco.i_multi_criteria_optimizers import IMultiCriteriaOptimizer
+
+
+class CoreMCODriver(Plugin):
+    """Main plugin that handles the execution of the MCO
+    or the evaluation.
+    """
+
+    # Note: we are forced to declare these extensions points here instead
+    # of the application object, and this is why we have to use this plugin.
+    # It is a workaround to an envisage bug that does not find the extension
+    # points if declared on the application.
+
+    #: A List of the available Multi Criteria Optimizers.
+    #: This will be populated by MCO plugins.
+    multi_criteria_optimizers = ExtensionPoint(
+        List(IMultiCriteriaOptimizer),
+        id='force_bdss.multi_criteria_optimizers')
+
+    #: A list of the available Key Performance Indicator calculators.
+    #: It will be populated by plugins.
+    key_performance_calculators = ExtensionPoint(
+        List(IKeyPerformanceCalculator),
+        id='force_bdss.key_performance_calculators')
+
+    @on_trait_change("application:started")
+    def application_started(self):
+        workflow = self.application.workflow
+        if self.application.evaluate:
+            for kpi in workflow.key_performance_indicators:
+                kpc = self._find_kpc_by_computes(kpi)
+                if kpc:
+                    kpc.run(self.application)
+                else:
+                    raise Exception("Requested KPI {} but don't know how"
+                                    "to compute it.".format(kpi))
+        else:
+            mco_name = workflow.multi_criteria_optimization.name
+            mco = self._find_mco_by_name(mco_name)
+            if mco:
+                mco.run(self.application)
+            else:
+                raise Exception("Requested MCO {} but it's not available"
+                                "to compute it.".format(mco_name))
+
+    def _find_kpc_by_computes(self, computes):
+        for kpc in self.key_performance_calculators:
+            if kpc.computes == computes:
+                return kpc
+
+        return None
+
+    def _find_mco_by_name(self, name):
+        for mco in self.multi_criteria_optimizers:
+            if mco.name == name:
+                return mco
+
+        return None
diff --git a/force_bdss/kpi/__init__.py b/force_bdss/kpi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/force_bdss/kpi/basic.py b/force_bdss/kpi/basic.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e0d021a3f10030fed5748ef11a1c8013149a158
--- /dev/null
+++ b/force_bdss/kpi/basic.py
@@ -0,0 +1,12 @@
+from traits.api import provides, HasStrictTraits, String
+
+from force_bdss.kpi.i_key_performance_calculator import (
+    IKeyPerformanceCalculator)
+
+
+@provides(IKeyPerformanceCalculator)
+class Basic(HasStrictTraits):
+    computes = String("basic")
+
+    def run(self, workflow):
+        print("Computing basic key performance indicator, {}".format(workflow))
diff --git a/force_bdss/kpi/i_key_performance_calculator.py b/force_bdss/kpi/i_key_performance_calculator.py
new file mode 100644
index 0000000000000000000000000000000000000000..56b7205c466180837c4ad7b80a89393fc8f2cdb0
--- /dev/null
+++ b/force_bdss/kpi/i_key_performance_calculator.py
@@ -0,0 +1,8 @@
+from traits.api import Interface, String
+
+
+class IKeyPerformanceCalculator(Interface):
+    computes = String()
+
+    def run(self):
+        pass
diff --git a/force_bdss/kpi/key_performance_calculators_plugin.py b/force_bdss/kpi/key_performance_calculators_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a646d36d129d29cee7fbf3c7f403a844a4f4818
--- /dev/null
+++ b/force_bdss/kpi/key_performance_calculators_plugin.py
@@ -0,0 +1,22 @@
+from envisage.plugin import Plugin
+from traits.api import List
+
+from force_bdss.kpi.i_key_performance_calculator import (
+    IKeyPerformanceCalculator)
+
+from force_bdss.kpi.basic import Basic
+from force_bdss.kpi.price import Price
+from force_bdss.kpi.viscosity import Viscosity
+
+
+class KeyPerformanceCalculatorsPlugin(Plugin):
+
+    id = "force_bdss.key_performance_calculators_plugin"
+
+    key_performance_calculators = List(
+        IKeyPerformanceCalculator,
+        contributes_to='force_bdss.key_performance_calculators'
+    )
+
+    def _key_performance_calculators_default(self):
+        return [Basic(), Viscosity(), Price()]
diff --git a/force_bdss/kpi/price.py b/force_bdss/kpi/price.py
new file mode 100644
index 0000000000000000000000000000000000000000..79112547824c97c0eb08954c26feddb13fccd5ea
--- /dev/null
+++ b/force_bdss/kpi/price.py
@@ -0,0 +1,12 @@
+from traits.api import provides, HasStrictTraits, String
+
+from force_bdss.kpi.i_key_performance_calculator import (
+    IKeyPerformanceCalculator)
+
+
+@provides(IKeyPerformanceCalculator)
+class Price(HasStrictTraits):
+    computes = String("price")
+
+    def run(self, workflow):
+        print("Computing price")
diff --git a/force_bdss/kpi/viscosity.py b/force_bdss/kpi/viscosity.py
new file mode 100644
index 0000000000000000000000000000000000000000..d8c3f3eac9214211903a6746b5a7884aa489ff54
--- /dev/null
+++ b/force_bdss/kpi/viscosity.py
@@ -0,0 +1,12 @@
+from traits.api import provides, HasStrictTraits, String
+
+from force_bdss.kpi.i_key_performance_calculator import (
+    IKeyPerformanceCalculator)
+
+
+@provides(IKeyPerformanceCalculator)
+class Viscosity(HasStrictTraits):
+    computes = String("viscosity")
+
+    def run(self, workflow):
+        print("Computing viscosity")
diff --git a/force_bdss/mco/__init__.py b/force_bdss/mco/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/force_bdss/mco/basic.py b/force_bdss/mco/basic.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e29c3bc233236dd41de6edad6f3e605c372b893
--- /dev/null
+++ b/force_bdss/mco/basic.py
@@ -0,0 +1,16 @@
+import subprocess
+import sys
+
+from traits.api import provides, HasStrictTraits, String
+
+from force_bdss.mco.i_multi_criteria_optimizers import IMultiCriteriaOptimizer
+
+
+@provides(IMultiCriteriaOptimizer)
+class Basic(HasStrictTraits):
+    name = String("basic")
+
+    def run(self, application):
+        print("Running Basic optimizer")
+        subprocess.check_call([sys.argv[0], "--evaluate",
+                               application.workflow_filepath])
diff --git a/force_bdss/mco/dakota.py b/force_bdss/mco/dakota.py
new file mode 100644
index 0000000000000000000000000000000000000000..b046b11f3cd407015593c92dc6e187695bee9b39
--- /dev/null
+++ b/force_bdss/mco/dakota.py
@@ -0,0 +1,17 @@
+import subprocess
+
+import sys
+
+from traits.api import provides, HasStrictTraits, String
+
+from force_bdss.mco.i_multi_criteria_optimizers import IMultiCriteriaOptimizer
+
+
+@provides(IMultiCriteriaOptimizer)
+class Dakota(HasStrictTraits):
+    name = String("dakota")
+
+    def run(self, application):
+        print("Running dakota optimizer")
+        subprocess.check_call([sys.argv[0], "--evaluate",
+                               application.workflow_filepath])
diff --git a/force_bdss/mco/i_multi_criteria_optimizers.py b/force_bdss/mco/i_multi_criteria_optimizers.py
new file mode 100644
index 0000000000000000000000000000000000000000..93c6b25dca4f73afc08caa0d348c59b9a2e195eb
--- /dev/null
+++ b/force_bdss/mco/i_multi_criteria_optimizers.py
@@ -0,0 +1,8 @@
+from traits.api import Interface, String
+
+
+class IMultiCriteriaOptimizer(Interface):
+    name = String()
+
+    def run(self, application):
+        pass
diff --git a/force_bdss/mco/multi_criteria_optimizers_plugin.py b/force_bdss/mco/multi_criteria_optimizers_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..79e3f3066fc1d589543de3ac384d221c2b7b0dba
--- /dev/null
+++ b/force_bdss/mco/multi_criteria_optimizers_plugin.py
@@ -0,0 +1,18 @@
+from envisage.plugin import Plugin
+from traits.api import List
+
+from force_bdss.mco.basic import Basic
+from force_bdss.mco.dakota import Dakota
+from force_bdss.mco.i_multi_criteria_optimizers import IMultiCriteriaOptimizer
+
+
+class MultiCriteriaOptimizersPlugin(Plugin):
+    id = "force_bdss.multi_criteria_optimizers_plugin"
+
+    multi_criteria_optimizers = List(
+        IMultiCriteriaOptimizer,
+        contributes_to='force_bdss.multi_criteria_optimizers'
+    )
+
+    def _multi_criteria_optimizers_default(self):
+        return [Basic(), Dakota()]
diff --git a/force_bdss/workspecs/multi_criteria_optimization.py b/force_bdss/workspecs/multi_criteria_optimization.py
new file mode 100644
index 0000000000000000000000000000000000000000..9e5e3152e68a168d0de2d2e5f0ff8d2031c1b25d
--- /dev/null
+++ b/force_bdss/workspecs/multi_criteria_optimization.py
@@ -0,0 +1,13 @@
+from traits.api import HasStrictTraits, String
+
+
+class MultiCriteriaOptimization(HasStrictTraits):
+    name = String()
+
+    @classmethod
+    def from_json(cls, json_data):
+        self = cls(
+            name=json_data["name"]
+        )
+
+        return self
diff --git a/force_bdss/workspecs/workflow.py b/force_bdss/workspecs/workflow.py
new file mode 100644
index 0000000000000000000000000000000000000000..934c7781d36daf2c80d3f680f1616573223b9c3e
--- /dev/null
+++ b/force_bdss/workspecs/workflow.py
@@ -0,0 +1,20 @@
+from traits.api import HasStrictTraits, Instance, String, List
+
+from .multi_criteria_optimization import MultiCriteriaOptimization
+
+
+class Workflow(HasStrictTraits):
+    name = String()
+    multi_criteria_optimization = Instance(MultiCriteriaOptimization)
+    key_performance_indicators = List(String)
+
+    @classmethod
+    def from_json(cls, json_data):
+        self = cls(
+            multi_criteria_optimization=MultiCriteriaOptimization.from_json(
+                    json_data["multi_criteria_optimization"]
+            ),
+            key_performance_indicators=json_data["key_performance_indicators"]
+            )
+
+        return self
diff --git a/requirements/dev_requirements.txt b/requirements/dev_requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..39304807fbc7bd47cb8a72ff873c076b694e4fbe
--- /dev/null
+++ b/requirements/dev_requirements.txt
@@ -0,0 +1 @@
+flake8
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2be76f218f7363cdffadd53391f60698221a5d61
--- /dev/null
+++ b/requirements/requirements.txt
@@ -0,0 +1,2 @@
+envisage==4.6.0
+click==6.7
diff --git a/setup.py b/setup.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c544f177233e07b060f03ec9bd59f0a2a8ac88dc 100644
--- a/setup.py
+++ b/setup.py
@@ -0,0 +1,16 @@
+from setuptools import setup
+
+VERSION = "0.1.0.dev0"
+
+setup(
+    name="force_bdss",
+    version=VERSION,
+    entry_points={
+        'console_scripts': [
+            'force_bdss = force_bdss.cli.force_bdss:run',
+        ]},
+    install_requires=[
+        "envisage >= 4.6.0",
+        "click >= 6.7"
+    ]
+)