diff --git a/api/apiIntegration_test.go b/api/apiIntegration_test.go
index 9839da725ee9a573f3afc768654ea40e126ddd7f..2b4de2dc543b751dc79fa8e64f50dd3e4cc93b03 100644
--- a/api/apiIntegration_test.go
+++ b/api/apiIntegration_test.go
@@ -4,6 +4,7 @@ import (
 	"os"
 	"testing"
 
+	"code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
 	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto"
 	guuid "github.com/google/uuid"
@@ -110,7 +111,7 @@ func TestApiIntegration(t *testing.T) {
 			cliPnd := viper.GetString("CLI_PND")
 			cliSbi := viper.GetString("CLI_SBI")
 
-			if _, err := AddDevice(
+			if _, err := addDevice(
 				testAPIEndpoint,
 				testUsername,
 				testPassword,
@@ -124,7 +125,7 @@ func TestApiIntegration(t *testing.T) {
 			}
 			did := viper.GetString("LAST_DEVICE_UUID")
 
-			_, err := GetDevice(
+			_, err := getDevice(
 				testAPIEndpoint,
 				cliPnd,
 				testPath,
@@ -135,7 +136,7 @@ func TestApiIntegration(t *testing.T) {
 				return
 			}
 
-			_, err = GetDevice(
+			_, err = getDevice(
 				testAPIEndpoint,
 				cliPnd,
 				"",
@@ -147,19 +148,20 @@ func TestApiIntegration(t *testing.T) {
 			}
 
 			hostname := guuid.New().String()
-			_, err = Update(
+			_, err = changeRequest(
 				testAPIEndpoint,
 				did,
 				cliPnd,
 				testPath,
 				hostname,
+				pnd.ApiOperation_UPDATE,
 			)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("gosdn cli set error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 
-			resp, err := GetDevice(testAddress, testUsername, testPassword, testPath)
+			resp, err := getDevice(testAddress, testUsername, testPassword, testPath)
 			if err != nil {
 				if !tt.wantErr {
 					t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/api/grpc_test.go b/api/api_test.go
similarity index 91%
rename from api/grpc_test.go
rename to api/api_test.go
index 549652a2ee0afd4939159ce37fa5cb39d2ecf72f..8bd94f6f5b6d2ee314c7c65d64d760362fd289f6 100644
--- a/api/grpc_test.go
+++ b/api/api_test.go
@@ -154,7 +154,7 @@ func Test_CommitConfirm(t *testing.T) {
 }
 
 func Test_AddDevice(t *testing.T) {
-	resp, err := AddDevice(bufnet, "test", "test", sbiID, pndID, "test", "test")
+	resp, err := addDevice(bufnet, "test", "test", sbiID, pndID, "test", "test")
 	if err != nil {
 		t.Error(err)
 		return
@@ -163,7 +163,7 @@ func Test_AddDevice(t *testing.T) {
 }
 
 func Test_GetDevice(t *testing.T) {
-	resp, err := GetDevice(bufnet, pndID, "", ondID)
+	resp, err := getDevice(bufnet, pndID, "", ondID)
 	if err != nil {
 		t.Error(err)
 		return
@@ -175,7 +175,7 @@ func Test_GetDevice(t *testing.T) {
 }
 
 func Test_Update(t *testing.T) {
-	resp, err := Update(bufnet, ondID, pndID, "", "")
+	resp, err := changeRequest(bufnet, ondID, pndID, "", "", ppb.ApiOperation_UPDATE)
 	if err != nil {
 		t.Error(err)
 		return
@@ -184,7 +184,7 @@ func Test_Update(t *testing.T) {
 }
 
 func Test_Replace(t *testing.T) {
-	resp, err := Replace(bufnet, ondID, pndID, "", "")
+	resp, err := changeRequest(bufnet, ondID, pndID, "", "", ppb.ApiOperation_REPLACE)
 	if err != nil {
 		t.Error(err)
 		return
@@ -193,7 +193,7 @@ func Test_Replace(t *testing.T) {
 }
 
 func Test_Delete(t *testing.T) {
-	resp, err := Delete(bufnet, ondID, pndID, "")
+	resp, err := changeRequest(bufnet, ondID, pndID, "", "", ppb.ApiOperation_DELETE)
 	if err != nil {
 		t.Error(err)
 		return
diff --git a/api/go.mod b/api/go.mod
index 0e0e8f2c94ce0c188d5d000abf9d84185009d28b..e3c75264d71e73e5a04413b5717b77667865118c 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -8,10 +8,9 @@ require (
 	code.fbi.h-da.de/cocsn/yang-models v0.0.7
 	github.com/google/uuid v1.2.0
 	github.com/openconfig/gnmi v0.0.0-20210527163611-d3a3e30199da
-	github.com/openconfig/goyang v0.2.4
-	github.com/openconfig/ygot v0.10.11
 	github.com/sirupsen/logrus v1.8.1
 	github.com/spf13/viper v1.7.1
 	github.com/stretchr/testify v1.7.0
 	google.golang.org/grpc v1.37.0
+	google.golang.org/protobuf v1.26.0
 )
diff --git a/api/go.sum b/api/go.sum
index 3054b8cd0aefee49f78fabf0040db1706546a087..697c8302ec0d6fb98a2900b54d8c6ea3c9d4bdce 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -171,7 +171,6 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
 github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/gnxi v0.0.0-20210423111716-4b504ef806a7 h1:cJ62uhbZcclaYm9gq4JNyazqSY7bUEggwZdw0nHTT7o=
 github.com/google/gnxi v0.0.0-20210423111716-4b504ef806a7/go.mod h1:dPTuHPVOqxZ2yGKPjymiMt1vrZa8KHXWKX+Lx1z5d88=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
diff --git a/api/grpc.go b/api/grpc.go
index 2c5dccc3fa56c908529e6cbf751f8fe1406bc631..e5e7c22802541a7d52e49b2ee00136c094e44b70 100644
--- a/api/grpc.go
+++ b/api/grpc.go
@@ -5,15 +5,13 @@ import (
 	"errors"
 	"time"
 
+	pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core"
 	ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
-	spb "code.fbi.h-da.de/cocsn/api/go/gosdn/southbound"
-	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
 	nbi "code.fbi.h-da.de/cocsn/gosdn/northbound/client"
-	"github.com/google/uuid"
+
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
-	pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core"
 	"google.golang.org/grpc"
 )
 
@@ -107,177 +105,3 @@ func GetPnd(addr string, args ...string) (*pb.GetResponse, error) {
 	}
 	return coreClient.Get(ctx, req)
 }
-
-// GetChanges requests all pending and unconfirmed changes from the controller
-func GetChanges(addr, pnd string) (*ppb.GetResponse, error) {
-	ctx := context.Background()
-	client, err := nbi.PndClient(addr, dialOptions...)
-	if err != nil {
-		return nil, err
-	}
-	req := &ppb.GetRequest{
-		Timestamp: time.Now().UnixNano(),
-		Request: &ppb.GetRequest_Change{
-			Change: &ppb.GetChange{
-				All: true,
-			},
-		},
-		Pid: pnd,
-	}
-	return client.Get(ctx, req)
-}
-
-// Commit sends a commit request for one or multiple changes to the
-// controller.
-func Commit(addr, pnd string, cuids ...string) (*ppb.SetResponse, error) {
-	changes := make([]*ppb.SetChange, len(cuids))
-	for i, arg := range cuids {
-		changes[i] = &ppb.SetChange{
-			Cuid: arg,
-			Op:   ppb.SetChange_COMMIT,
-		}
-	}
-	return commitConfirm(addr, pnd, changes)
-}
-
-// Confirm sends a confirm request for one or multiple changes to the
-// controller
-func Confirm(addr, pnd string, cuids ...string) (*ppb.SetResponse, error) {
-	changes := make([]*ppb.SetChange, len(cuids))
-	for i, arg := range cuids {
-		changes[i] = &ppb.SetChange{
-			Cuid: arg,
-			Op:   ppb.SetChange_CONFIRM,
-		}
-	}
-	return commitConfirm(addr, pnd, changes)
-}
-
-func commitConfirm(addr, pnd string, changes []*ppb.SetChange) (*ppb.SetResponse, error) {
-	ctx := context.Background()
-	client, err := nbi.PndClient(addr, dialOptions...)
-	if err != nil {
-		return nil, err
-	}
-	req := &ppb.SetRequest{
-		Timestamp: time.Now().UnixNano(),
-		Change:    changes,
-		Pid:       pnd,
-	}
-	return client.Set(ctx, req)
-}
-
-// AddDevice adds a new device to the controller. The device name is optional.
-// If no name is provided a name will be generated upon device creation.
-func AddDevice(addr, username, password, sbi, pnd, deviceAddress, deviceName string) (*ppb.SetResponse, error) {
-	pndClient, err := nbi.PndClient(addr, dialOptions...)
-	if err != nil {
-		return nil, err
-	}
-
-	req := &ppb.SetRequest{
-		Timestamp: time.Now().UnixNano(),
-		Ond: []*ppb.SetOnd{
-			{
-				Address: deviceAddress,
-				Sbi: &spb.SouthboundInterface{
-					Id: sbi,
-				},
-				DeviceName: deviceName,
-				TransportOption: &tpb.TransportOption{
-					Address:  deviceAddress,
-					Username: username,
-					Password: password,
-					TransportOption: &tpb.TransportOption_GnmiTransportOption{
-						GnmiTransportOption: &tpb.GnmiTransportOption{},
-					},
-				},
-			},
-		},
-		Pid: pnd,
-	}
-	if sbi == "csbi" {
-		req.Ond[0].Sbi.Id = uuid.Nil.String()
-		req.Ond[0].Sbi.Type = spb.Type_CONTAINERISED
-		req.Ond[0].TransportOption.Csbi = true
-	}
-	ctx := context.Background()
-	return pndClient.Set(ctx, req)
-}
-
-// GetDevice requests one or multiple devices belonging to a given
-// PrincipalNetworkDomain from the controller. If no device identifier
-// is provided, all devices are requested.
-func GetDevice(addr, pid, path string, did ...string) (*ppb.GetResponse, error) {
-	pndClient, err := nbi.PndClient(addr, dialOptions...)
-	if err != nil {
-		return nil, err
-	}
-
-	var all bool
-	if len(did) == 0 {
-		all = true
-	}
-
-	req := &ppb.GetRequest{
-		Timestamp: time.Now().UnixNano(),
-		Request: &ppb.GetRequest_Ond{
-			Ond: &ppb.GetOnd{
-				All: all,
-				Did: did,
-			},
-		},
-		Pid: pid,
-	}
-	ctx := context.Background()
-	return pndClient.Get(ctx, req)
-}
-
-// Update creates a ChangeRequest to update the given path with the given value
-// at the given OND on the controller.
-func Update(addr, did, pid, path, value string) (*ppb.SetResponse, error) {
-	req := &ppb.ChangeRequest{
-		Id:    did,
-		Path:  path,
-		Value: value,
-		ApiOp: ppb.ApiOperation_UPDATE,
-	}
-	return sendChangeRequest(addr, pid, req)
-}
-
-// Replace creates a ChangeRequest to replace the given path with the given value
-// at the given OND on the controller.
-func Replace(addr, did, pid, path, value string) (*ppb.SetResponse, error) {
-	req := &ppb.ChangeRequest{
-		Id:    did,
-		Path:  path,
-		Value: value,
-		ApiOp: ppb.ApiOperation_REPLACE,
-	}
-	return sendChangeRequest(addr, pid, req)
-}
-
-// Delete creates a ChangeRequest to delete the given path node
-// at the given OND on the controller.
-func Delete(addr, did, pid, path string) (*ppb.SetResponse, error) {
-	req := &ppb.ChangeRequest{
-		Id:    did,
-		Path:  path,
-		ApiOp: ppb.ApiOperation_DELETE,
-	}
-	return sendChangeRequest(addr, pid, req)
-}
-
-func sendChangeRequest(addr, pid string, req *ppb.ChangeRequest) (*ppb.SetResponse, error) {
-	pndClient, err := nbi.PndClient(addr, dialOptions...)
-	if err != nil {
-		return nil, err
-	}
-	ctx := context.Background()
-	r := &ppb.SetRequest{
-		Timestamp:     time.Now().UnixNano(),
-		ChangeRequest: []*ppb.ChangeRequest{req},
-		Pid:           pid,
-	}
-	return pndClient.Set(ctx, r)
-}
diff --git a/api/pnd.go b/api/pnd.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cab2dfb430dd12c9ee93fc30189f658c5d493dc
--- /dev/null
+++ b/api/pnd.go
@@ -0,0 +1,260 @@
+package api
+
+import (
+	"context"
+	"time"
+
+	ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
+	spb "code.fbi.h-da.de/cocsn/api/go/gosdn/southbound"
+	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
+	"code.fbi.h-da.de/cocsn/gosdn/interfaces/change"
+	"code.fbi.h-da.de/cocsn/gosdn/interfaces/device"
+	"code.fbi.h-da.de/cocsn/gosdn/interfaces/southbound"
+	"code.fbi.h-da.de/cocsn/gosdn/interfaces/store"
+	nbi "code.fbi.h-da.de/cocsn/gosdn/northbound/client"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
+	"github.com/google/uuid"
+)
+
+type PrincipalNetworkDomainAdapter struct {
+	id       uuid.UUID
+	endpoint string
+}
+
+func (p *PrincipalNetworkDomainAdapter) Destroy() error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) AddSbi(s southbound.SouthboundInterface) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) RemoveSbi(uuid.UUID) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) AddDevice(name string, opts *tpb.TransportOption, sid uuid.UUID) error {
+
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) GetDevice(identifier string) (device.Device, error) {
+	return nil, &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) RemoveDevice(uuid.UUID) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) Devices() []uuid.UUID {
+	return nil
+}
+
+func (p *PrincipalNetworkDomainAdapter) ChangeOND(uuid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) Request(uuid.UUID, string) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) RequestAll(string) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) GetName() string {
+	return ""
+}
+
+func (p *PrincipalNetworkDomainAdapter) GetDescription() string {
+	return ""
+}
+
+func (p *PrincipalNetworkDomainAdapter) MarshalDevice(string) (string, error) {
+	return "", &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) ContainsDevice(uuid.UUID) bool {
+	return false
+}
+
+func (p *PrincipalNetworkDomainAdapter) GetSBIs() store.Store {
+	return nil
+}
+
+func (p *PrincipalNetworkDomainAdapter) ID() uuid.UUID {
+	return p.id
+}
+
+func (p *PrincipalNetworkDomainAdapter) PendingChanges() []uuid.UUID {
+	return nil
+}
+
+func (p *PrincipalNetworkDomainAdapter) CommittedChanges() []uuid.UUID {
+	return nil
+}
+
+func (p *PrincipalNetworkDomainAdapter) GetChange(uuid.UUID, ...int) (change.Change, error) {
+	return nil, &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) Commit(uuid.UUID) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+func (p *PrincipalNetworkDomainAdapter) Confirm(uuid.UUID) error {
+	return &errors.ErrNotYetImplemented{}
+}
+
+// GetChanges requests all pending and unconfirmed changes from the controller
+func GetChanges(addr, pnd string) (*ppb.GetResponse, error) {
+	ctx := context.Background()
+	client, err := nbi.PndClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+	req := &ppb.GetRequest{
+		Timestamp: time.Now().UnixNano(),
+		Request: &ppb.GetRequest_Change{
+			Change: &ppb.GetChange{
+				All: true,
+			},
+		},
+		Pid: pnd,
+	}
+	return client.Get(ctx, req)
+}
+
+// Commit sends a commit request for one or multiple changes to the
+// controller.
+func Commit(addr, pnd string, cuids ...string) (*ppb.SetResponse, error) {
+	changes := make([]*ppb.SetChange, len(cuids))
+	for i, arg := range cuids {
+		changes[i] = &ppb.SetChange{
+			Cuid: arg,
+			Op:   ppb.SetChange_COMMIT,
+		}
+	}
+	return commitConfirm(addr, pnd, changes)
+}
+
+// Confirm sends a confirm request for one or multiple changes to the
+// controller
+func Confirm(addr, pnd string, cuids ...string) (*ppb.SetResponse, error) {
+	changes := make([]*ppb.SetChange, len(cuids))
+	for i, arg := range cuids {
+		changes[i] = &ppb.SetChange{
+			Cuid: arg,
+			Op:   ppb.SetChange_CONFIRM,
+		}
+	}
+	return commitConfirm(addr, pnd, changes)
+}
+
+func commitConfirm(addr, pnd string, changes []*ppb.SetChange) (*ppb.SetResponse, error) {
+	ctx := context.Background()
+	client, err := nbi.PndClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+	req := &ppb.SetRequest{
+		Timestamp: time.Now().UnixNano(),
+		Change:    changes,
+		Pid:       pnd,
+	}
+	return client.Set(ctx, req)
+}
+
+// addDevice adds a new device to the controller. The device name is optional.
+// If no name is provided a name will be generated upon device creation.
+func addDevice(addr, username, password, sbi, pnd, deviceAddress, deviceName string) (*ppb.SetResponse, error) {
+	pndClient, err := nbi.PndClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &ppb.SetRequest{
+		Timestamp: time.Now().UnixNano(),
+		Ond: []*ppb.SetOnd{
+			{
+				Address: deviceAddress,
+				Sbi: &spb.SouthboundInterface{
+					Id: sbi,
+				},
+				DeviceName: deviceName,
+				TransportOption: &tpb.TransportOption{
+					Address:  deviceAddress,
+					Username: username,
+					Password: password,
+					TransportOption: &tpb.TransportOption_GnmiTransportOption{
+						GnmiTransportOption: &tpb.GnmiTransportOption{},
+					},
+				},
+			},
+		},
+		Pid: pnd,
+	}
+	if sbi == "csbi" {
+		req.Ond[0].Sbi.Id = uuid.Nil.String()
+		req.Ond[0].Sbi.Type = spb.Type_CONTAINERISED
+		req.Ond[0].TransportOption.Csbi = true
+	}
+	ctx := context.Background()
+	return pndClient.Set(ctx, req)
+}
+
+// getDevice requests one or multiple devices belonging to a given
+// PrincipalNetworkDomain from the controller. If no device identifier
+// is provided, all devices are requested.
+func getDevice(addr, pid, path string, did ...string) (*ppb.GetResponse, error) {
+	pndClient, err := nbi.PndClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	var all bool
+	if len(did) == 0 {
+		all = true
+	}
+
+	req := &ppb.GetRequest{
+		Timestamp: time.Now().UnixNano(),
+		Request: &ppb.GetRequest_Ond{
+			Ond: &ppb.GetOnd{
+				All: all,
+				Did: did,
+			},
+		},
+		Pid: pid,
+	}
+	ctx := context.Background()
+	return pndClient.Get(ctx, req)
+}
+
+// change creates a ChangeRequest for the specified OND. ApiOperations are
+// used to specify the type of the change (update, replace, delete as specified
+// in https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#34-modifying-state)
+// For delete operations the value field needs to contain an empty string.
+func changeRequest(addr, did, pid, path, value string, op ppb.ApiOperation) (*ppb.SetResponse, error) {
+	req := &ppb.ChangeRequest{
+		Id:    did,
+		Path:  path,
+		Value: value,
+		ApiOp: op,
+	}
+	return sendChangeRequest(addr, pid, req)
+}
+
+func sendChangeRequest(addr, pid string, req *ppb.ChangeRequest) (*ppb.SetResponse, error) {
+	pndClient, err := nbi.PndClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+	ctx := context.Background()
+	r := &ppb.SetRequest{
+		Timestamp:     time.Now().UnixNano(),
+		ChangeRequest: []*ppb.ChangeRequest{req},
+		Pid:           pid,
+	}
+	return pndClient.Set(ctx, r)
+}