DatabaseService that uses Copycat Raft to provide a strongly consistent and durable database.
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
new file mode 100644
index 0000000..3c92800
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
@@ -0,0 +1,144 @@
+package org.onlab.onos.store.service.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import net.kuujo.copycat.protocol.Response.Status;
+import net.kuujo.copycat.protocol.SubmitRequest;
+import net.kuujo.copycat.protocol.SubmitResponse;
+import net.kuujo.copycat.spi.protocol.ProtocolClient;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.onlab.netty.Endpoint;
+import org.onlab.netty.NettyMessagingService;
+import org.onlab.onos.store.service.DatabaseException;
+import org.onlab.onos.store.service.ReadRequest;
+import org.onlab.onos.store.service.WriteRequest;
+
+public class DatabaseClient {
+
+    private final Endpoint copycatEp;
+    ProtocolClient client;
+    NettyMessagingService messagingService;
+
+    public DatabaseClient(Endpoint copycatEp) {
+        this.copycatEp = copycatEp;
+    }
+
+    private static String nextId() {
+        return UUID.randomUUID().toString();
+    }
+
+    public void activate() throws Exception {
+        messagingService = new NettyMessagingService(RandomUtils.nextInt(10000, 40000));
+        messagingService.activate();
+        client = new NettyProtocolClient(copycatEp, messagingService);
+    }
+
+    public void deactivate() throws Exception {
+        messagingService.deactivate();
+    }
+
+    public boolean createTable(String tableName) {
+
+        SubmitRequest request =
+                new SubmitRequest(
+                        nextId(),
+                        "createTable",
+                        Arrays.asList(tableName));
+        CompletableFuture<SubmitResponse> future = client.submit(request);
+        try {
+            return (boolean) future.get().result();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    public void dropTable(String tableName) {
+
+        SubmitRequest request =
+                new SubmitRequest(
+                        nextId(),
+                        "dropTable",
+                        Arrays.asList(tableName));
+        CompletableFuture<SubmitResponse> future = client.submit(request);
+        try {
+            if (future.get().status() == Status.OK) {
+                throw new DatabaseException(future.get().toString());
+            }
+
+        } catch (InterruptedException | ExecutionException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    public void dropAllTables() {
+
+        SubmitRequest request =
+                new SubmitRequest(
+                        nextId(),
+                        "dropAllTables",
+                        Arrays.asList());
+        CompletableFuture<SubmitResponse> future = client.submit(request);
+        try {
+            if (future.get().status() != Status.OK) {
+                throw new DatabaseException(future.get().toString());
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<String> listTables() {
+
+        SubmitRequest request =
+                new SubmitRequest(
+                        nextId(),
+                        "listTables",
+                        Arrays.asList());
+        CompletableFuture<SubmitResponse> future = client.submit(request);
+        try {
+            return (List<String>) future.get().result();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<InternalReadResult> batchRead(List<ReadRequest> requests) {
+
+        SubmitRequest request = new SubmitRequest(
+                        nextId(),
+                        "read",
+                        Arrays.asList(requests));
+
+        CompletableFuture<SubmitResponse> future = client.submit(request);
+        try {
+            List<InternalReadResult> internalReadResults = (List<InternalReadResult>) future.get().result();
+            return internalReadResults;
+        } catch (InterruptedException | ExecutionException e) {
+            throw new DatabaseException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<InternalWriteResult> batchWrite(List<WriteRequest> requests) {
+
+        SubmitRequest request = new SubmitRequest(
+                        nextId(),
+                        "write",
+                        Arrays.asList(requests));
+
+        CompletableFuture<SubmitResponse> future = client.submit(request);
+        try {
+            List<InternalWriteResult> internalWriteResults = (List<InternalWriteResult>) future.get().result();
+            return internalWriteResults;
+        } catch (InterruptedException | ExecutionException e) {
+            throw new DatabaseException(e);
+        }
+    }
+}