diff --git a/controller.go b/controller.go
index 5adfd95a34442351b0f0b9044f9ba2350bc42c99..46d20105c9ea0371adabe81e874acb5275983c82 100644
--- a/controller.go
+++ b/controller.go
@@ -14,7 +14,7 @@ import (
 	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
 	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
 	"code.fbi.h-da.de/cocsn/gosdn/database"
-	"code.fbi.h-da.de/cocsn/gosdn/northbound"
+	nbi "code.fbi.h-da.de/cocsn/gosdn/northbound/server"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
 	"github.com/google/uuid"
diff --git a/http.go b/http.go
index 92ce1890a2a0d5585d0093f6614fa727d919184c..d672ababce485d7868ab4c6304e6890484a23fee 100644
--- a/http.go
+++ b/http.go
@@ -1,27 +1,13 @@
 package gosdn
 
 import (
-	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
 	"context"
 	"fmt"
-	"github.com/google/uuid"
-	gpb "github.com/openconfig/gnmi/proto/gnmi"
 	log "github.com/sirupsen/logrus"
-	"io"
 	"net/http"
-	"net/url"
 	"time"
 )
 
-var apiOpmap = map[string]types.Operation{
-	"update":  types.TransportUpdate,
-	"replace": types.TransportReplace,
-	"delete":  types.TransportDelete,
-}
-
 func stopHttpServer() error {
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
@@ -35,7 +21,6 @@ func registerHttpHandler() {
 			fmt.Println("Recovered in f", r)
 		}
 	}()
-	http.HandleFunc("/api", httpApi)
 	http.HandleFunc("/livez", healthCheck)
 	http.HandleFunc("/readyz", readynessCheck)
 }
@@ -55,190 +40,3 @@ func healthCheck(writer http.ResponseWriter, request *http.Request) {
 func readynessCheck(writer http.ResponseWriter, request *http.Request) {
 	writer.WriteHeader(http.StatusOK)
 }
-
-// deprecated
-// nolint
-func httpApi(writer http.ResponseWriter, request *http.Request) {
-	log.WithFields(log.Fields{
-		"request": request,
-	}).Debug("incoming request")
-
-	query, err := url.ParseQuery(request.URL.RawQuery)
-	if err != nil {
-		log.Error(err)
-		writer.WriteHeader(http.StatusBadRequest)
-		return
-	}
-
-	id, err := uuid.Parse(query.Get("uuid"))
-	if err != nil {
-		if err.Error() != "invalid UUID length: 0" {
-			log.Error(err)
-		}
-	}
-
-	pid, err := uuid.Parse(query.Get("pnd"))
-	if err != nil {
-		if err.Error() != "invalid UUID length: 0" {
-			log.Error(err)
-		}
-	}
-
-	sid, err := uuid.Parse(query.Get("sbi"))
-	if err != nil {
-		if err.Error() != "invalid UUID length: 0" {
-			log.Error(err)
-		}
-	}
-
-	var httpPnd nucleus.PrincipalNetworkDomain
-	var httpSbi nucleus.SouthboundInterface
-	if query.Get("q") != "init" && query.Get("q") != "getIDs" {
-		httpPnd, err = c.pndc.Get(pid)
-		if err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		sbic := httpPnd.GetSBIs()
-		httpSbi, err = sbic.(*nucleus.SbiStore).Get(sid)
-		if err != nil {
-			handleServerError(writer, err)
-			return
-		}
-	}
-
-	switch q := query.Get("q"); q {
-	case "addDevice":
-		d, err := nucleus.NewDevice(httpSbi, &nucleus.GnmiTransportOptions{
-			Config: gnmi.Config{
-				Addr:     query.Get("address"),
-				Password: query.Get("password"),
-				Username: query.Get("username"),
-				Encoding: gpb.Encoding_JSON_IETF,
-			},
-			SetNode:   httpSbi.SetNode(),
-			Unmarshal: httpSbi.(*nucleus.OpenConfig).Unmarshal(),
-			RespChan:  make(chan *gpb.SubscribeResponse),
-		})
-		err = httpPnd.AddDevice(d)
-		if err != nil {
-			writer.WriteHeader(http.StatusInternalServerError)
-			log.Error(err)
-			return
-		}
-		writer.WriteHeader(http.StatusCreated)
-		fmt.Fprintf(writer, "device added\n")
-		fmt.Fprintf(writer, "UUID: %v\n", d.UUID)
-	case "request":
-		err = httpPnd.Request(id, query.Get("path"))
-		if err != nil {
-			switch err.(type) {
-			case *errors.ErrNotFound:
-				writer.WriteHeader(http.StatusNotFound)
-			default:
-				writer.WriteHeader(http.StatusInternalServerError)
-			}
-			log.Error(err)
-			return
-		}
-		writer.WriteHeader(http.StatusOK)
-	case "requestAll":
-		err = httpPnd.RequestAll(query.Get("path"))
-		if err != nil {
-			switch err.(type) {
-			case *errors.ErrNotFound:
-				writer.WriteHeader(http.StatusNotFound)
-			default:
-				writer.WriteHeader(http.StatusInternalServerError)
-			}
-			log.Error(err)
-			return
-		}
-		writer.WriteHeader(http.StatusOK)
-	case "getDevice":
-		device, err := httpPnd.MarshalDevice(id)
-		if err != nil {
-			switch err.(type) {
-			case *errors.ErrNotFound:
-				writer.WriteHeader(http.StatusNotFound)
-			default:
-				writer.WriteHeader(http.StatusInternalServerError)
-			}
-			log.Error(err)
-			return
-		}
-		writer.Header().Set("Content-Type", "application/json")
-		fmt.Fprintf(writer, "%v", device)
-	case "getIDs":
-
-		pnds := c.pndc.UUIDs()
-		writeIDs(writer, "PNDs", pnds)
-		writeIDs(writer, "SBIs", c.sbic.UUIDs())
-		for _, id := range pnds {
-			p, err := c.pndc.Get(id)
-			if err != nil {
-				handleServerError(writer, err)
-				return
-			}
-			writeIDs(writer, "Devices", p.Devices())
-		}
-	case "init":
-		writeIDs(writer, "PNDs", c.pndc.UUIDs())
-		writeIDs(writer, "SBIs", c.sbic.UUIDs())
-	case "update", "replace":
-		if err := httpPnd.ChangeOND(id, apiOpmap[q], query.Get("path"), query.Get("value")); err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		writer.WriteHeader(http.StatusOK)
-	case "delete":
-		if err := httpPnd.ChangeOND(id, types.TransportDelete, query.Get("path")); err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		writer.WriteHeader(http.StatusOK)
-	case "change-list":
-		changes := httpPnd.Committed()
-		writeIDs(writer, "Tentative changes", changes)
-	case "change-list-pending":
-		changes := httpPnd.Pending()
-		writeIDs(writer, "Pending changes", changes)
-	case "change-commit":
-		cuid, err := uuid.Parse(query.Get("cuid"))
-		if err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		if err := httpPnd.Commit(cuid); err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		writer.WriteHeader(http.StatusAccepted)
-	case "change-confirm":
-		cuid, err := uuid.Parse(query.Get("cuid"))
-		if err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		if err := httpPnd.Confirm(cuid); err != nil {
-			handleServerError(writer, err)
-			return
-		}
-		writer.WriteHeader(http.StatusAccepted)
-	default:
-		writer.WriteHeader(http.StatusBadRequest)
-	}
-}
-
-func writeIDs(w io.Writer, typ string, ids []uuid.UUID) {
-	fmt.Fprintf(w, "%v:\n", typ)
-	for i, id := range ids {
-		fmt.Fprintf(w, "%v: %v\n", i+1, id)
-	}
-}
-
-func handleServerError(w http.ResponseWriter, err error) {
-	w.WriteHeader(http.StatusInternalServerError)
-	fmt.Fprintf(w, "error: %v", err)
-	log.Error(err)
-}
diff --git a/http_test.go b/http_test.go
index 47a7a3e12ecc20da64c1f5476ea938d6783c44d7..94092e1ca63a862359cd9ca9ba4ad2fa753b4721 100644
--- a/http_test.go
+++ b/http_test.go
@@ -1,48 +1,10 @@
 package gosdn
 
 import (
-	"errors"
 	"net/http"
 	"testing"
-
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
-
-	"code.fbi.h-da.de/cocsn/gosdn/mocks"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	"github.com/google/uuid"
-	log "github.com/sirupsen/logrus"
-	"github.com/stretchr/testify/mock"
 )
 
-func testSetupHTTP() {
-	sbi = nucleus.NewSBI(types.Openconfig)
-	sbi.Schema()
-	defaultSbiID = sbi.ID()
-	var err error
-	httpTestPND, err = nucleus.NewPND("test", "test pnd", defaultPndID, sbi)
-	if err != nil {
-		log.Fatal(err)
-	}
-	httpTestDevice = mockDevice()
-	tr := httpTestDevice.Transport.(*mocks.Transport)
-	mockError := errors.New("mock error")
-	tr.On("Get", mockContext, "/system/config/hostname").Return(mock.Anything, nil)
-	tr.On("Get", mockContext, "error").Return(mock.Anything, mockError)
-	tr.On("Set", mockContext, mock.Anything).Return(mock.Anything, nil)
-	tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	if err := httpTestPND.AddDevice(&httpTestDevice); err != nil {
-		log.Fatal(err)
-	}
-	args = "&uuid=" + mdid.String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String()
-	argsNotFound = "&uuid=" + uuid.New().String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String()
-	if err := c.sbic.Add(sbi); err != nil {
-		log.Fatal(err)
-	}
-	if err := c.pndc.Add(httpTestPND); err != nil {
-		log.Fatal(err)
-	}
-}
-
 func Test_httpApi(t *testing.T) {
 	tests := []struct {
 		name    string
@@ -62,129 +24,6 @@ func Test_httpApi(t *testing.T) {
 			want:    &http.Response{StatusCode: http.StatusOK},
 			wantErr: false,
 		},
-		{
-			name:    "init",
-			request: apiEndpoint + "/api?q=init",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "get-ids",
-			request: apiEndpoint + "/api?q=getIDs",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "add-device",
-			request: apiEndpoint + "/api?q=addDevice" + args,
-			want:    &http.Response{StatusCode: http.StatusCreated},
-			wantErr: false,
-		},
-		{
-			name:    "request",
-			request: apiEndpoint + "/api?q=request" + args + "&path=/system/config/hostname",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "request not found",
-			request: apiEndpoint + "/api?q=request" + argsNotFound + "&path=/system/config/hostname",
-			want:    &http.Response{StatusCode: http.StatusNotFound},
-			wantErr: false,
-		},
-		{
-			name:    "request internal server error",
-			request: apiEndpoint + "/api?q=request" + args + "&path=error",
-			want:    &http.Response{StatusCode: http.StatusInternalServerError},
-			wantErr: false,
-		},
-		{
-			name:    "request-all",
-			request: apiEndpoint + "/api?q=requestAll" + args + "&path=/system/config/hostname",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "request-all internal server error",
-			request: apiEndpoint + "/api?q=requestAll" + args + "&path=error",
-			want:    &http.Response{StatusCode: http.StatusInternalServerError},
-			wantErr: false,
-		},
-
-		{
-			name:    "get-device",
-			request: apiEndpoint + "/api?q=getDevice" + args,
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "get-device not found",
-			request: apiEndpoint + "/api?q=getDevice" + argsNotFound,
-			want:    &http.Response{StatusCode: http.StatusNotFound},
-			wantErr: false,
-		},
-		{
-			name:    "set",
-			request: apiEndpoint + "/api?q=update" + args + "&path=/system/config/hostname&value=ceos3000",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "replace",
-			request: apiEndpoint + "/api?q=replace" + args + "&path=/system/config/hostname&value=ceos3000",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "delete",
-			request: apiEndpoint + "/api?q=delete" + args + "&path=/system/config/hostname",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "change list",
-			request: apiEndpoint + "/api?q=change-list" + args + "&path=/system/config/hostname",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "change list pending",
-			request: apiEndpoint + "/api?q=change-list-pending" + args + "&path=/system/config/hostname",
-			want:    &http.Response{StatusCode: http.StatusOK},
-			wantErr: false,
-		},
-		{
-			name:    "change commit",
-			request: apiEndpoint + "/api?q=change-commit" + args + "&cuid=" + cuid.String(),
-			// TODO: Mock Change for testing
-			want:    &http.Response{StatusCode: http.StatusInternalServerError},
-			wantErr: false,
-		},
-		{
-			name:    "change confirm",
-			request: apiEndpoint + "/api?q=change-confirm" + args + "&cuid=" + cuid.String(),
-			// TODO: Mock Change for testing
-			want:    &http.Response{StatusCode: http.StatusInternalServerError},
-			wantErr: false,
-		},
-		{
-			name:    "bad request",
-			request: apiEndpoint + "/api?q=bad-request" + args,
-			want:    &http.Response{StatusCode: http.StatusBadRequest},
-			wantErr: false,
-		},
-		{
-			name:    "internal server errror: wrong pnd",
-			request: apiEndpoint + "/api?pnd=" + uuid.New().String(),
-			want:    &http.Response{StatusCode: http.StatusInternalServerError},
-			wantErr: false,
-		},
-		{
-			name:    "internal server errror: wrong sbi",
-			request: apiEndpoint + "/api?sbi=" + uuid.New().String(),
-			want:    &http.Response{StatusCode: http.StatusInternalServerError},
-			wantErr: false,
-		},
 	}
 	coreLock.Lock()
 	startHttpServer()
@@ -199,16 +38,6 @@ func Test_httpApi(t *testing.T) {
 			if got.StatusCode != tt.want.StatusCode {
 				t.Errorf("httpApi() got: %v, want %v", got.StatusCode, tt.want.StatusCode)
 			}
-			if tt.name == "add-device" {
-				for _, k := range httpTestPND.Devices() {
-					if k != mdid {
-						if err := httpTestPND.RemoveDevice(k); err != nil {
-							t.Error(err)
-							return
-						}
-					}
-				}
-			}
 		})
 	}
 }
diff --git a/initialise_test.go b/initialise_test.go
index 5fbb298a7aa6a4cbc30aff8a80695aa1647eb34a..828a27730795b46353d71825c605828ab3316023 100644
--- a/initialise_test.go
+++ b/initialise_test.go
@@ -63,7 +63,6 @@ func TestMain(m *testing.M) {
 	}
 	readTestUUIDs()
 
-	testSetupHTTP()
 	os.Exit(m.Run())
 }
 
diff --git a/northbound/client.go b/northbound/client.go
deleted file mode 100644
index d0b2c91c94937844ed08444c7e8f53ea94583ffc..0000000000000000000000000000000000000000
--- a/northbound/client.go
+++ /dev/null
@@ -1 +0,0 @@
-package nbi
diff --git a/northbound/client/client.go b/northbound/client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..da13c8ef3ca33e1788a7b9560e0ade4e46b5f6f6
--- /dev/null
+++ b/northbound/client/client.go
@@ -0,0 +1 @@
+package client
diff --git a/northbound/server.go b/northbound/server.go
deleted file mode 100644
index 1463f95b9823b3e55321544867ea3a06b8687ba6..0000000000000000000000000000000000000000
--- a/northbound/server.go
+++ /dev/null
@@ -1,169 +0,0 @@
-package nbi
-
-import (
-	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
-	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
-	"context"
-	"github.com/google/uuid"
-	"github.com/openconfig/ygot/ygot"
-	"sync"
-	"time"
-)
-
-var pndLock sync.RWMutex
-var sbiLock sync.RWMutex
-var pndc *nucleus.PndStore
-var sbic *nucleus.SbiStore
-
-type pndServer struct {
-	ppb.UnimplementedPndServer
-}
-
-func (p pndServer) Get(ctx context.Context, request *ppb.GetRequest) (*ppb.GetResponse, error) {
-	switch request.Type {
-	case ppb.GetRequest_PND:
-		pid, err := uuid.Parse(request.Id)
-		if err != nil {
-			return nil, err
-		}
-		return handlePndRequest(pid)
-	case ppb.GetRequest_OND:
-		return handleOndRequest()
-	case ppb.GetRequest_ONDS:
-		return handleOndsRequest()
-	case ppb.GetRequest_SBI:
-		return handleSbiRequest()
-	case ppb.GetRequest_SBIS:
-		return handleSbisRequest()
-	case ppb.GetRequest_Changes:
-		return handleChangesRequest()
-	default:
-		return nil, errors.ErrOperationNotSupported{Op: request.Type}
-	}
-}
-
-func handlePndRequest(pid uuid.UUID) (*ppb.GetResponse, error) {
-	pndLock.RLock()
-	defer pndLock.RUnlock()
-	pnd, err := pndc.Get(pid)
-	if err != nil {
-		return nil, err
-	}
-	onds, err := fillOnds(pnd)
-	if err != nil {
-		return nil, err
-	}
-	sbis, err := fillSbis(pnd)
-	return &ppb.GetResponse{
-		Timestamp: time.Now().UnixNano(),
-		Payload: &ppb.GetResponse_Pnd{
-			Pnd: &ppb.PrincipalNetworkDomain{
-				Id:          pid.String(),
-				Name:        pnd.GetName(),
-				Description: pnd.GetDescription(),
-				Onds:        onds,
-				Sbis:        sbis,
-				Changes:     nil,
-			},
-		},
-	}, nil
-}
-
-func handleSbiRequest() (*ppb.GetResponse, error) {
-	return nil, errors.ErrNotYetImplemented{}
-}
-
-func handleSbisRequest() (*ppb.GetResponse, error) {
-	return nil, errors.ErrNotYetImplemented{}
-}
-
-func handleOndRequest() (*ppb.GetResponse, error) {
-	return nil, errors.ErrNotYetImplemented{}
-}
-
-func handleOndsRequest() (*ppb.GetResponse, error) {
-	return nil, errors.ErrNotYetImplemented{}
-}
-
-func handleChangesRequest() (*ppb.GetResponse, error) {
-	return nil, errors.ErrNotYetImplemented{}
-}
-
-func fillOnds(pnd nucleus.PrincipalNetworkDomain) (*ppb.OrchestratedNetworkingDevices, error) {
-	onds := make(map[string]*ppb.OrchestratedNetworkingDevice)
-	for _, id := range pnd.Devices() {
-		d, err := pnd.GetDevice(id)
-		if err != nil {
-			return nil, err
-		}
-		cfg := ygot.GNMINotificationsConfig{}
-		dev, err := ygot.TogNMINotifications(d, time.Now().UnixNano(), cfg)
-		if err != nil {
-			return nil, err
-		}
-		ond := &ppb.OrchestratedNetworkingDevice{
-			Id:     id.String(),
-			Name:   "",
-			Device: dev,
-		}
-		onds[id.String()] = ond
-	}
-	return &ppb.OrchestratedNetworkingDevices{Onds: onds}, nil
-}
-
-func fillSbis(pnd nucleus.PrincipalNetworkDomain) (*ppb.SouthboundInterfaces, error) {
-	sbis := make(map[string]*ppb.SouthboundInterface)
-	sbiLock.RLock()
-	defer sbiLock.RUnlock()
-	sbic := pnd.GetSBIs().(*nucleus.SbiStore)
-	for _,id := range sbic.UUIDs() {
-		sbi, err := sbic.Get(id)
-		if err != nil {
-			return nil, err
-		}
-		sbis[id.String()] = &ppb.SouthboundInterface{
-			Id:     id.String(),
-			Schema: sbi.Schema().RootSchema().Name,
-		}
-	}
-	return &ppb.SouthboundInterfaces{Sbis: sbis}, nil
-}
-
-func fillChanges(pnd nucleus.PrincipalNetworkDomain) (*ppb.Changes, error) {
-	// TODO: Allow access to change at leaso on
-	return nil, errors.ErrNotYetImplemented{}
-}
-
-func (p pndServer) Set(ctx context.Context, request *ppb.SetRequest) (*ppb.SetResponse, error) {
-	panic("implement me")
-}
-
-type gosdnServer struct {
-	pb.UnimplementedGosdnServer
-}
-
-func (s gosdnServer) Get(ctx context.Context, request *pb.GetRequest) (*pb.GetResponse, error) {
-	panic("implement me")
-}
-
-func (s gosdnServer) Set(ctx context.Context, request *pb.SetRequest) (*pb.SetResponse, error) {
-	panic("implement me")
-}
-
-func NewNBI(pnds *nucleus.PndStore, sbis *nucleus.SbiStore) *NorthboundInterface {
-	pndc = pnds
-	sbic = sbis
-	pndLock = sync.RWMutex{}
-	sbiLock = sync.RWMutex{}
-	return &NorthboundInterface{
-		Pnd:        &pndServer{},
-		Controller: &gosdnServer{},
-	}
-}
-
-type NorthboundInterface struct {
-	Pnd        *pndServer
-	Controller *gosdnServer
-}
diff --git a/northbound/server/pnd.go b/northbound/server/pnd.go
new file mode 100644
index 0000000000000000000000000000000000000000..f080fc1ac36035613f25c6050a35ccf99da3914a
--- /dev/null
+++ b/northbound/server/pnd.go
@@ -0,0 +1,187 @@
+package server
+
+import (
+	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
+	"context"
+	"github.com/google/uuid"
+	"github.com/openconfig/ygot/ygot"
+	"reflect"
+	"time"
+)
+
+type pndServer struct {
+	ppb.UnimplementedPndServer
+}
+
+func (p pndServer) Get(ctx context.Context, request *ppb.GetRequest) (*ppb.GetResponse, error) {
+	pid, err := uuid.Parse(request.Pid)
+	if err != nil {
+		return nil, err
+	}
+	switch req := request.Request.(type) {
+	case *ppb.GetRequest_Pnd:
+		return handleGetPnd(pid)
+	case *ppb.GetRequest_Ond:
+		return handleGetOnd(pid, req)
+	case *ppb.GetRequest_Sbi:
+		return handleGetSbi(pid, req)
+	case *ppb.GetRequest_Change:
+		return handleGetChange(pid, req)
+	default:
+		return nil, errors.ErrOperationNotSupported{Op: reflect.TypeOf(request.Request)}
+	}
+}
+
+func handleGetPnd(pid uuid.UUID) (*ppb.GetResponse, error) {
+	pndLock.RLock()
+	defer pndLock.RUnlock()
+	pnd, err := pndc.Get(pid)
+	if err != nil {
+		return nil, err
+	}
+	onds, err := fillOnds(pnd, true)
+	if err != nil {
+		return nil, err
+	}
+	sbis, err := fillSbis(pnd, true)
+	return &ppb.GetResponse{
+		Timestamp: time.Now().UnixNano(),
+		Pnd: &ppb.PrincipalNetworkDomain{
+			Id:          "",
+			Name:        "",
+			Description: "",
+			Ond:         onds,
+			Sbi:         sbis,
+			Change:      nil,
+		},
+	}, nil
+}
+
+func handleGetSbi(pid uuid.UUID, req *ppb.GetRequest_Sbi) (*ppb.GetResponse, error) {
+	pndLock.RLock()
+	defer pndLock.RUnlock()
+	pnd, err := pndc.Get(pid)
+	if err != nil {
+		return nil, err
+	}
+	sbis, err := fillSbis(pnd, req.Sbi.GetAll(), req.Sbi.Sid...)
+	return &ppb.GetResponse{
+		Timestamp: time.Now().UnixNano(),
+		Sbi:       sbis,
+	}, nil
+}
+
+func stringToUUID(sid []string) ([]uuid.UUID, error) {
+	UUIDs := make([]uuid.UUID, len(sid))
+	for i, id := range sid {
+		parsed, err := uuid.Parse(id)
+		if err != nil {
+			return nil, err
+		}
+		UUIDs[i] = parsed
+	}
+	return UUIDs, nil
+}
+
+func handleGetOnd(pid uuid.UUID, req *ppb.GetRequest_Ond) (*ppb.GetResponse, error) {
+	pndLock.RLock()
+	defer pndLock.RUnlock()
+	pnd, err := pndc.Get(pid)
+	if err != nil {
+		return nil, err
+	}
+	onds, err := fillOnds(pnd, req.Ond.All, req.Ond.Did...)
+	return &ppb.GetResponse{
+		Timestamp: time.Now().UnixNano(),
+		Ond:       onds,
+	}, nil
+}
+
+func handleGetChange(pid uuid.UUID, req *ppb.GetRequest_Change) (*ppb.GetResponse, error) {
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func fillSbis(pnd nucleus.PrincipalNetworkDomain, all bool, sid ...string) ([]*ppb.SouthboundInterface, error) {
+	var sbiList []uuid.UUID
+
+	sbiStore := pnd.GetSBIs().(*nucleus.SbiStore)
+	switch all {
+	case true:
+		sbiList = sbiStore.UUIDs()
+	default:
+		var err error
+		if len(sid) == 0 {
+			return nil, &errors.ErrInvalidParameters{
+				Func:  fillSbis,
+				Param: "lenth of 'sid' cannot be '0' when 'all' is set to 'false'",
+			}
+		}
+		sbiList, err = stringToUUID(sid)
+		if err != nil {
+			return nil, err
+		}
+	}
+	sbis := make([]*ppb.SouthboundInterface, len(sbiStore.UUIDs()))
+	for i, id := range sbiList {
+		sbi, err := sbiStore.Get(id)
+		if err != nil {
+			return nil, err
+		}
+		sbis[i] = &ppb.SouthboundInterface{
+			Id:     id.String(),
+			Schema: sbi.Schema().RootSchema().Name,
+		}
+	}
+	return sbis, nil
+}
+
+func fillOnds(pnd nucleus.PrincipalNetworkDomain, all bool, did ...string) ([]*ppb.OrchestratedNetworkingDevice, error) {
+	var ondList []uuid.UUID
+
+	switch all {
+	case true:
+		ondList = pnd.Devices()
+	default:
+		var err error
+		if len(did) == 0 {
+			return nil, &errors.ErrInvalidParameters{
+				Func:  fillOnds,
+				Param: "lenth of 'did' cannot be '0' when 'all' is set to 'false'",
+			}
+		}
+		ondList, err = stringToUUID(did)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	onds := make([]*ppb.OrchestratedNetworkingDevice, len(pnd.Devices()))
+	for i, id := range ondList {
+		d, err := pnd.GetDevice(id)
+		if err != nil {
+			return nil, err
+		}
+		cfg := ygot.GNMINotificationsConfig{}
+		dev, err := ygot.TogNMINotifications(d, time.Now().UnixNano(), cfg)
+		if err != nil {
+			return nil, err
+		}
+		onds[i] = &ppb.OrchestratedNetworkingDevice{
+			Id:     id.String(),
+			Name:   "",
+			Device: dev,
+		}
+	}
+	return onds, nil
+}
+
+func fillChanges(pnd nucleus.PrincipalNetworkDomain) ([]*ppb.Change, error) {
+	// TODO: Allow access to changes
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func (p pndServer) Set(ctx context.Context, request *ppb.SetRequest) (*ppb.SetResponse, error) {
+	panic("implement me")
+}
diff --git a/northbound/server/server.go b/northbound/server/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..592504b0a42b79437f5581935386ccffeef03a73
--- /dev/null
+++ b/northbound/server/server.go
@@ -0,0 +1,41 @@
+package server
+
+import (
+	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
+	"context"
+	"sync"
+)
+
+var pndLock sync.RWMutex
+var sbiLock sync.RWMutex
+var pndc *nucleus.PndStore
+var sbic *nucleus.SbiStore
+
+type gosdnServer struct {
+	pb.UnimplementedGosdnServer
+}
+
+func (s gosdnServer) Get(ctx context.Context, request *pb.GetRequest) (*pb.GetResponse, error) {
+	panic("implement me")
+}
+
+func (s gosdnServer) Set(ctx context.Context, request *pb.SetRequest) (*pb.SetResponse, error) {
+	panic("implement me")
+}
+
+func NewNBI(pnds *nucleus.PndStore, sbis *nucleus.SbiStore) *NorthboundInterface {
+	pndc = pnds
+	sbic = sbis
+	pndLock = sync.RWMutex{}
+	sbiLock = sync.RWMutex{}
+	return &NorthboundInterface{
+		Pnd:        &pndServer{},
+		Controller: &gosdnServer{},
+	}
+}
+
+type NorthboundInterface struct {
+	Pnd        *pndServer
+	Controller *gosdnServer
+}
diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go
index d5f7c369d40eb384de23049ea771e474d7d0a7fe..2ba89bf6fe2fc02c01124efb6d63e7d457ffd92a 100644
--- a/nucleus/gnmi_transport.go
+++ b/nucleus/gnmi_transport.go
@@ -79,7 +79,7 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
 	}
 	if len(args) == 0 {
 		return &errors.ErrInvalidParameters{
-			Func:  "gnmi.Set()",
+			Func:  "nucleus.Set()",
 			Param: "no parameters provided",
 		}
 	}
@@ -103,7 +103,7 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
 			}
 		} else if attrs == nil || len(attrs) == 0 {
 			return &errors.ErrInvalidParameters{
-				Func:  "gnmi.Set()",
+				Func:  "nucleus.Set()",
 				Param: "no parameters provided",
 			}
 		}
@@ -133,14 +133,14 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
 			exts = append(exts, p.(*gnmi_ext.Extension))
 		default:
 			return &errors.ErrInvalidParameters{
-				Func:  "gnmi.Set()",
+				Func:  "nucleus.Set()",
 				Param: "args contain invalid type",
 			}
 		}
 	}
 	if len(ops) == 0 {
 		return &errors.ErrInvalidParameters{
-			Func:  "gnmi.Set()",
+			Func:  "nucleus.Set()",
 			Param: "no operations provided",
 		}
 	}