Repository: edgexfoundry/device-sdk-go
Pull request #1345 (2023-02-23 13:22:30)

refactor!: remove global variable 'ds' in service package
BREAKING CHANGE:
- update ProtocolDriver Initialize method signature to pass
DeviceServiceSDK interface as parameter
- update DeviceServiceSDK interface:
    - update Getter method name to be more idiomatic
    - remove Stop() method as it should only be called by SDK

this commit tried to remove the DeviceService global variable, the
RunningService() and Service() function that accessed it.

Instead, the DeviceServiceSDK interface is passed to ProtocolDriver as
a paramter in Initialize method so that developer can still access, mock and
test with it.

closes https://github.com/edgexfoundry/device-sdk-go/issues/1304

Signed-off-by: Chris Hung <chris@iotechsys.com>

<!-- Expected Commit Message Description (imported automatically by GitHub) -->
<!-- Must conform to [conventional commits guidelines](https://github.com/edgexfoundry/device-sdk-go/blob/main/.github/Contributing.md) -->
<!-- Expected Commit message must contain Closes/Fixes #IssueNumber statement when there is a related issue -->

<!-- Add additional detailed description of need for change if no related issue -->

**If your build fails** due to your commit message not passing the build checks, please review the guidelines here: https://github.com/edgexfoundry/device-sdk-go/blob/main/.github/Contributing.md

## PR Checklist
Please check if your PR fulfills the following requirements:

- [ ] I am not introducing a breaking change (if you are, flag in conventional commit message with `BREAKING CHANGE:` describing the break)
- [x] I am not introducing a new dependency (add notes below if you are)
- [ ] I have added unit tests for the new feature or bug fix (if not, why?)
- [x] I have fully tested (add details below) this the new feature or bug fix (if not, why?)
- [ ] I have opened a PR for the related docs change (if not, why?)
  <link to docs PR>

## Testing Instructions
<!-- How can the reviewers test your change? -->
1. Run `device-simple` and verify it works correctly

## New Dependency Instructions (If applicable)
<!-- Please follow [vetting instructions](https://wiki.edgexfoundry.org/display/FA/Vetting+Process+for+3rd+Party+Dependencies) and place results here -->

codecov-commenter: # [Codecov](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) Report
> Merging [#1345](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) (915ebad) into [main](https://codecov.io/gh/edgexfoundry/device-sdk-go/commit/b7afc2aba7a1c5861c6c21c42b42dc1283ea5949?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) (b7afc2a) will **increase** coverage by `0.12%`.
> The diff coverage is `1.43%`.

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry)

```diff
@@            Coverage Diff             @@
##             main    #1345      +/-   ##
==========================================
+ Coverage   47.48%   47.60%   +0.12%     
==========================================
  Files          28       27       -1     
  Lines        2622     2615       -7     
==========================================
  Hits         1245     1245              
+ Misses       1268     1261       -7     
  Partials      109      109              
```


| [Impacted Files](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry) | Coverage Δ | |
|---|---|---|
| [internal/controller/http/restrouter.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-aW50ZXJuYWwvY29udHJvbGxlci9odHRwL3Jlc3Ryb3V0ZXIuZ28=) | `73.91% <ø> (ø)` | |
| [pkg/service/init.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2UvaW5pdC5nbw==) | `0.00% <0.00%> (ø)` | |
| [pkg/service/managedautoevents.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2UvbWFuYWdlZGF1dG9ldmVudHMuZ28=) | `0.00% <0.00%> (ø)` | |
| [pkg/service/manageddevices.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2UvbWFuYWdlZGRldmljZXMuZ28=) | `0.00% <0.00%> (ø)` | |
| [pkg/service/managedprofiles.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2UvbWFuYWdlZHByb2ZpbGVzLmdv) | `0.00% <0.00%> (ø)` | |
| [pkg/service/managedwatchers.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2UvbWFuYWdlZHdhdGNoZXJzLmdv) | `0.00% <0.00%> (ø)` | |
| [pkg/service/service.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2Uvc2VydmljZS5nbw==) | `0.00% <0.00%> (ø)` | |
| [pkg/service/async.go](https://codecov.io/gh/edgexfoundry/device-sdk-go/pull/1345?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry#diff-cGtnL3NlcnZpY2UvYXN5bmMuZ28=) | `27.50% <25.00%> (+2.50%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=edgexfoundry)


Patch:
diff --git a/example/driver/simpledriver.go b/example/driver/simpledriver.go
index 2ba58242..17c2492c 100644
--- a/example/driver/simpledriver.go
+++ b/example/driver/simpledriver.go
@@ -5,7 +5,7 @@
 //
 // SPDX-License-Identifier: Apache-2.0
 
-// This package provides a simple example implementation of
+// Package driver provides a simple example implementation of
 // ProtocolDriver interface.
 package driver
 
@@ -34,6 +34,7 @@ import (
 const readCommandsExecutedName = "ReadCommandsExecuted"
 
 type SimpleDriver struct {
+	sdk                  interfaces.DeviceServiceSDK
 	lc                   logger.LoggingClient
 	asyncCh              chan<- *sdkModels.AsyncValues
 	deviceCh             chan<- []sdkModels.DiscoveredDevice
@@ -84,10 +85,11 @@ func getImageBytes(imgFile string, buf *bytes.Buffer) error {
 
 // Initialize performs protocol-specific initialization for the device
 // service.
-func (s *SimpleDriver) Initialize(lc logger.LoggingClient, asyncCh chan<- *sdkModels.AsyncValues, deviceCh chan<- []sdkModels.DiscoveredDevice) error {
-	s.lc = lc
-	s.asyncCh = asyncCh
-	s.deviceCh = deviceCh
+func (s *SimpleDriver) Initialize(sdk interfaces.DeviceServiceSDK) error {
+	s.sdk = sdk
+	s.lc = sdk.LoggingClient()
+	s.asyncCh = sdk.AsyncValuesChannel()
+	s.deviceCh = sdk.DiscoveredDeviceChannel()
 	s.serviceConfig = &config.ServiceConfig{}
 	s.counter = map[string]interface{}{
 		"f1": "ABC",
@@ -95,19 +97,17 @@ func (s *SimpleDriver) Initialize(lc logger.LoggingClient, asyncCh chan<- *sdkMo
 	}
 	s.stringArray = []string{"foo", "bar"}
 
-	ds := interfaces.Service()
-
-	if err := ds.LoadCustomConfig(s.serviceConfig, "SimpleCustom"); err != nil {
+	if err := sdk.LoadCustomConfig(s.serviceConfig, "SimpleCustom"); err != nil {
 		return fmt.Errorf("unable to load 'SimpleCustom' custom configuration: %s", err.Error())
 	}
 
-	lc.Infof("Custom config is: %v", s.serviceConfig.SimpleCustom)
+	s.lc.Infof("Custom config is: %v", s.serviceConfig.SimpleCustom)
 
 	if err := s.serviceConfig.SimpleCustom.Validate(); err != nil {
 		return fmt.Errorf("'SimpleCustom' custom configuration validation failed: %s", err.Error())
 	}
 
-	if err := ds.ListenForCustomConfigChanges(
+	if err := sdk.ListenForCustomConfigChanges(
 		&s.serviceConfig.SimpleCustom.Writable,
 		"SimpleCustom/Writable", s.ProcessCustomConfigChanges); err != nil {
 		return fmt.Errorf("unable to listen for changes for 'SimpleCustom.Writable' custom configuration: %s", err.Error())
@@ -116,7 +116,7 @@ func (s *SimpleDriver) Initialize(lc logger.LoggingClient, asyncCh chan<- *sdkMo
 	s.readCommandsExecuted = gometrics.NewCounter()
 
 	var err error
-	metricsManger := ds.GetMetricsManager()
+	metricsManger := sdk.MetricsManager()
 	if metricsManger != nil {
 		err = metricsManger.Register(readCommandsExecutedName, s.readCommandsExecuted, nil)
 	} else {
diff --git a/internal/application/callback.go b/internal/application/callback.go
index 2401414f..7727c888 100644
--- a/internal/application/callback.go
+++ b/internal/application/callback.go
@@ -79,7 +79,7 @@ func AddDevice(addDeviceRequest requests.AddDeviceRequest, dic *di.Container) er
 	}
 
 	lc.Debugf("starting AutoEvents for device %s", device.Name)
-	container.ManagerFrom(dic.Get).RestartForDevice(device.Name)
+	container.AutoEventManagerFrom(dic.Get).RestartForDevice(device.Name)
 	return nil
 }
 
@@ -129,7 +129,7 @@ func UpdateDevice(updateDeviceRequest requests.UpdateDeviceRequest, dic *di.Cont
 		return errors.NewCommonEdgeX(errors.KindServerError, errMsg, err)
 	}
 
-	autoEventManager := container.ManagerFrom(dic.Get)
+	autoEventManager := container.AutoEventManagerFrom(dic.Get)
 	if device.AdminState == models.Locked {
 		lc.Debugf("stopping AutoEvents for the locked device %s", device.Name)
 		autoEventManager.StopForDevice(device.Name)
@@ -146,7 +146,7 @@ func DeleteDevice(name string, dic *di.Container) errors.EdgeX {
 	device, ok := cache.Devices().ForName(name)
 	if ok {
 		lc.Debugf("stopping AutoEvents for device %s", device.Name)
-		container.ManagerFrom(dic.Get).StopForDevice(device.Name)
+		container.AutoEventManagerFrom(dic.Get).StopForDevice(device.Name)
 	} else {
 		errMsg := fmt.Sprintf("failed to find device %s", name)
 		return errors.NewCommonEdgeX(errors.KindInvalidId, errMsg, nil)
diff --git a/internal/autodiscovery/discovery.go b/internal/autodiscovery/discovery.go
index 005cc3b6..61ca64a4 100644
--- a/internal/autodiscovery/discovery.go
+++ b/internal/autodiscovery/discovery.go
@@ -11,7 +11,7 @@ import (
 
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 
-	"github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
+	"github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces"
 )
 
 type discoveryLocker struct {
@@ -21,7 +21,7 @@ type discoveryLocker struct {
 
 var locker discoveryLocker
 
-func DiscoveryWrapper(discovery models.ProtocolDiscovery, lc logger.LoggingClient) {
+func DiscoveryWrapper(discovery interfaces.ProtocolDiscovery, lc logger.LoggingClient) {
 	locker.mux.Lock()
 	if locker.busy {
 		lc.Info("another device discovery process is currently running")
diff --git a/internal/autoevent/manager.go b/internal/autoevent/manager.go
index b3b3140e..15f6b707 100644
--- a/internal/autoevent/manager.go
+++ b/internal/autoevent/manager.go
@@ -43,7 +43,7 @@ func BootstrapHandler(
 	}
 
 	dic.Update(di.ServiceConstructorMap{
-		container.ManagerName: func(get di.Get) interface{} {
+		container.AutoEventManagerName: func(get di.Get) interface{} {
 			return m
 		},
 	})
diff --git a/internal/clients/clients.go b/internal/clients/clients.go
deleted file mode 100644
index 8373f799..00000000
--- a/internal/clients/clients.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// -*- mode: Go; indent-tabs-mode: t -*-
-//
-// Copyright (C) 2020 IOTech Ltd
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package clients
-
-import (
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/interfaces"
-)
-
-type EdgeXClients struct {
-	DeviceClient           interfaces.DeviceClient
-	DeviceServiceClient    interfaces.DeviceServiceClient
-	DeviceProfileClient    interfaces.DeviceProfileClient
-	ProvisionWatcherClient interfaces.ProvisionWatcherClient
-	EventClient            interfaces.EventClient
-}
diff --git a/internal/container/deviceservice.go b/internal/container/deviceservice.go
index ac81e086..6376c64c 100644
--- a/internal/container/deviceservice.go
+++ b/internal/container/deviceservice.go
@@ -10,23 +10,23 @@ import (
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
 
-	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
+	"github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces"
 )
 
 // DeviceServiceName contains the name of device service struct in the DIC.
 var DeviceServiceName = di.TypeInstanceToName(models.DeviceService{})
 
 // ProtocolDiscoveryName contains the name of protocol discovery implementation in the DIC.
-var ProtocolDiscoveryName = di.TypeInstanceToName((*sdkModels.ProtocolDiscovery)(nil))
+var ProtocolDiscoveryName = di.TypeInstanceToName((*interfaces.ProtocolDiscovery)(nil))
 
 // ProtocolDriverName contains the name of protocol driver implementation in the DIC.
-var ProtocolDriverName = di.TypeInstanceToName((*sdkModels.ProtocolDriver)(nil))
+var ProtocolDriverName = di.TypeInstanceToName((*interfaces.ProtocolDriver)(nil))
 
-// ManagerName contains the name of autoevent manager implementation in the DIC
-var ManagerName = di.TypeInstanceToName((*sdkModels.AutoEventManager)(nil))
+// AutoEventManagerName contains the name of autoevent manager implementation in the DIC
+var AutoEventManagerName = di.TypeInstanceToName((*interfaces.AutoEventManager)(nil))
 
 // DeviceValidatorName contains the name of device validator implementation in the DIC.
-var DeviceValidatorName = di.TypeInstanceToName((*sdkModels.DeviceValidator)(nil))
+var DeviceValidatorName = di.TypeInstanceToName((*interfaces.DeviceValidator)(nil))
 
 // DeviceServiceFrom helper function queries the DIC and returns device service struct.
 func DeviceServiceFrom(get di.Get) *models.DeviceService {
@@ -34,8 +34,8 @@ func DeviceServiceFrom(get di.Get) *models.DeviceService {
 }
 
 // ProtocolDiscoveryFrom helper function queries the DIC and returns protocol discovery implementation.
-func ProtocolDiscoveryFrom(get di.Get) sdkModels.ProtocolDiscovery {
-	casted, ok := get(ProtocolDiscoveryName).(sdkModels.ProtocolDiscovery)
+func ProtocolDiscoveryFrom(get di.Get) interfaces.ProtocolDiscovery {
+	casted, ok := get(ProtocolDiscoveryName).(interfaces.ProtocolDiscovery)
 	if ok {
 		return casted
 	}
@@ -43,8 +43,8 @@ func ProtocolDiscoveryFrom(get di.Get) sdkModels.ProtocolDiscovery {
 }
 
 // DeviceValidatorFrom helper function queries the DIC and returns device validator implementation.
-func DeviceValidatorFrom(get di.Get) sdkModels.DeviceValidator {
-	casted, ok := get(DeviceValidatorName).(sdkModels.DeviceValidator)
+func DeviceValidatorFrom(get di.Get) interfaces.DeviceValidator {
+	casted, ok := get(DeviceValidatorName).(interfaces.DeviceValidator)
 	if ok {
 		return casted
 	}
@@ -52,11 +52,11 @@ func DeviceValidatorFrom(get di.Get) sdkModels.DeviceValidator {
 }
 
 // ProtocolDriverFrom helper function queries the DIC and returns protocol driver implementation.
-func ProtocolDriverFrom(get di.Get) sdkModels.ProtocolDriver {
-	return get(ProtocolDriverName).(sdkModels.ProtocolDriver)
+func ProtocolDriverFrom(get di.Get) interfaces.ProtocolDriver {
+	return get(ProtocolDriverName).(interfaces.ProtocolDriver)
 }
 
-// ManagerFrom helper function queries the DIC and returns autoevent manager implementation
-func ManagerFrom(get di.Get) sdkModels.AutoEventManager {
-	return get(ManagerName).(sdkModels.AutoEventManager)
+// AutoEventManagerFrom helper function queries the DIC and returns autoevent manager implementation
+func AutoEventManagerFrom(get di.Get) interfaces.AutoEventManager {
+	return get(AutoEventManagerName).(interfaces.AutoEventManager)
 }
diff --git a/internal/controller/http/restrouter.go b/internal/controller/http/restrouter.go
index 73f1a63f..cc0ac779 100644
--- a/internal/controller/http/restrouter.go
+++ b/internal/controller/http/restrouter.go
@@ -29,12 +29,12 @@ import (
 )
 
 type RestController struct {
-	lc             logger.LoggingClient
+	serviceName    string
 	router         *mux.Router
 	reservedRoutes map[string]bool
-	dic            *di.Container
-	serviceName    string
 	customConfig   interfaces.UpdatableConfig
+	lc             logger.LoggingClient
+	dic            *di.Container
 }
 
 func NewRestController(r *mux.Router, dic *di.Container, serviceName string) *RestController {
diff --git a/internal/controller/messaging/callback.go b/internal/controller/messaging/callback.go
index d06c94b2..91f2bf0f 100644
--- a/internal/controller/messaging/callback.go
+++ b/internal/controller/messaging/callback.go
@@ -22,7 +22,7 @@ import (
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
 )
 
-func MetadataSystemEventCallback(ctx context.Context, dic *di.Container) errors.EdgeX {
+func MetadataSystemEventsCallback(ctx context.Context, dic *di.Container) errors.EdgeX {
 	lc := bootstrapContainer.LoggingClientFrom(dic.Get)
 	messageBusInfo := container.ConfigurationFrom(dic.Get).MessageBus
 	deviceService := container.DeviceServiceFrom(dic.Get)
diff --git a/pkg/models/devicevalidator.go b/pkg/interfaces/devicevalidator.go
similarity index 96%
rename from pkg/models/devicevalidator.go
rename to pkg/interfaces/devicevalidator.go
index e26c8d8a..c995ed91 100644
--- a/pkg/models/devicevalidator.go
+++ b/pkg/interfaces/devicevalidator.go
@@ -3,7 +3,7 @@
 //
 // SPDX-License-Identifier: Apache-2.0
 
-package models
+package interfaces
 
 import "github.com/edgexfoundry/go-mod-core-contracts/v3/models"
 
diff --git a/pkg/models/manager.go b/pkg/interfaces/manager.go
similarity index 95%
rename from pkg/models/manager.go
rename to pkg/interfaces/manager.go
index 591a9320..96e00fca 100644
--- a/pkg/models/manager.go
+++ b/pkg/interfaces/manager.go
@@ -3,7 +3,7 @@
 //
 // SPDX-License-Identifier: Apache-2.0
 
-package models
+package interfaces
 
 type AutoEventManager interface {
 	// StartAutoEvents starts all the AutoEvents of the device service
diff --git a/pkg/interfaces/mocks/DeviceServiceSDK.go b/pkg/interfaces/mocks/DeviceServiceSDK.go
index ee0a31c2..e8293d9e 100644
--- a/pkg/interfaces/mocks/DeviceServiceSDK.go
+++ b/pkg/interfaces/mocks/DeviceServiceSDK.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.20.2. DO NOT EDIT.
 
 package mocks
 
@@ -7,13 +7,15 @@ import (
 
 	bootstrapinterfaces "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces"
 
+	interfaces "github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces"
+
 	logger "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 
 	mock "github.com/stretchr/testify/mock"
 
 	models "github.com/edgexfoundry/go-mod-core-contracts/v3/models"
 
-	service "github.com/edgexfoundry/device-sdk-go/v3/pkg/service"
+	pkgmodels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
 )
 
 // DeviceServiceSDK is an autogenerated mock type for the DeviceServiceSDK type
@@ -26,13 +28,16 @@ func (_m *DeviceServiceSDK) AddDevice(device models.Device) (string, error) {
 	ret := _m.Called(device)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(models.Device) (string, error)); ok {
+		return rf(device)
+	}
 	if rf, ok := ret.Get(0).(func(models.Device) string); ok {
 		r0 = rf(device)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(models.Device) error); ok {
 		r1 = rf(device)
 	} else {
@@ -61,13 +66,16 @@ func (_m *DeviceServiceSDK) AddDeviceProfile(profile models.DeviceProfile) (stri
 	ret := _m.Called(profile)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(models.DeviceProfile) (string, error)); ok {
+		return rf(profile)
+	}
 	if rf, ok := ret.Get(0).(func(models.DeviceProfile) string); ok {
 		r0 = rf(profile)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(models.DeviceProfile) error); ok {
 		r1 = rf(profile)
 	} else {
@@ -82,13 +90,16 @@ func (_m *DeviceServiceSDK) AddProvisionWatcher(watcher models.ProvisionWatcher)
 	ret := _m.Called(watcher)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(models.ProvisionWatcher) (string, error)); ok {
+		return rf(watcher)
+	}
 	if rf, ok := ret.Get(0).(func(models.ProvisionWatcher) string); ok {
 		r0 = rf(watcher)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(models.ProvisionWatcher) error); ok {
 		r1 = rf(watcher)
 	} else {
@@ -119,8 +130,8 @@ func (_m *DeviceServiceSDK) AddRoute(route string, handler func(http.ResponseWri
 	return r0
 }
 
-// AsyncReadings provides a mock function with given fields:
-func (_m *DeviceServiceSDK) AsyncReadings() bool {
+// AsyncReadingsEnabled provides a mock function with given fields:
+func (_m *DeviceServiceSDK) AsyncReadingsEnabled() bool {
 	ret := _m.Called()
 
 	var r0 bool
@@ -133,18 +144,37 @@ func (_m *DeviceServiceSDK) AsyncReadings() bool {
 	return r0
 }
 
+// AsyncValuesChannel provides a mock function with given fields:
+func (_m *DeviceServiceSDK) AsyncValuesChannel() chan *pkgmodels.AsyncValues {
+	ret := _m.Called()
+
+	var r0 chan *pkgmodels.AsyncValues
+	if rf, ok := ret.Get(0).(func() chan *pkgmodels.AsyncValues); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(chan *pkgmodels.AsyncValues)
+		}
+	}
+
+	return r0
+}
+
 // DeviceCommand provides a mock function with given fields: deviceName, commandName
 func (_m *DeviceServiceSDK) DeviceCommand(deviceName string, commandName string) (models.DeviceCommand, bool) {
 	ret := _m.Called(deviceName, commandName)
 
 	var r0 models.DeviceCommand
+	var r1 bool
+	if rf, ok := ret.Get(0).(func(string, string) (models.DeviceCommand, bool)); ok {
+		return rf(deviceName, commandName)
+	}
 	if rf, ok := ret.Get(0).(func(string, string) models.DeviceCommand); ok {
 		r0 = rf(deviceName, commandName)
 	} else {
 		r0 = ret.Get(0).(models.DeviceCommand)
 	}
 
-	var r1 bool
 	if rf, ok := ret.Get(1).(func(string, string) bool); ok {
 		r1 = rf(deviceName, commandName)
 	} else {
@@ -154,8 +184,8 @@ func (_m *DeviceServiceSDK) DeviceCommand(deviceName string, commandName string)
 	return r0, r1
 }
 
-// DeviceDiscovery provides a mock function with given fields:
-func (_m *DeviceServiceSDK) DeviceDiscovery() bool {
+// DeviceDiscoveryEnabled provides a mock function with given fields:
+func (_m *DeviceServiceSDK) DeviceDiscoveryEnabled() bool {
 	ret := _m.Called()
 
 	var r0 bool
@@ -189,13 +219,16 @@ func (_m *DeviceServiceSDK) DeviceResource(deviceName string, deviceResource str
 	ret := _m.Called(deviceName, deviceResource)
 
 	var r0 models.DeviceResource
+	var r1 bool
+	if rf, ok := ret.Get(0).(func(string, string) (models.DeviceResource, bool)); ok {
+		return rf(deviceName, deviceResource)
+	}
 	if rf, ok := ret.Get(0).(func(string, string) models.DeviceResource); ok {
 		r0 = rf(deviceName, deviceResource)
 	} else {
 		r0 = ret.Get(0).(models.DeviceResource)
 	}
 
-	var r1 bool
 	if rf, ok := ret.Get(1).(func(string, string) bool); ok {
 		r1 = rf(deviceName, deviceResource)
 	} else {
@@ -221,6 +254,22 @@ func (_m *DeviceServiceSDK) Devices() []models.Device {
 	return r0
 }
 
+// DiscoveredDeviceChannel provides a mock function with given fields:
+func (_m *DeviceServiceSDK) DiscoveredDeviceChannel() chan []pkgmodels.DiscoveredDevice {
+	ret := _m.Called()
+
+	var r0 chan []pkgmodels.DiscoveredDevice
+	if rf, ok := ret.Get(0).(func() chan []pkgmodels.DiscoveredDevice); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(chan []pkgmodels.DiscoveredDevice)
+		}
+	}
+
+	return r0
+}
+
 // DriverConfigs provides a mock function with given fields:
 func (_m *DeviceServiceSDK) DriverConfigs() map[string]string {
 	ret := _m.Called()
@@ -242,13 +291,16 @@ func (_m *DeviceServiceSDK) GetDeviceByName(name string) (models.Device, error)
 	ret := _m.Called(name)
 
 	var r0 models.Device
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (models.Device, error)); ok {
+		return rf(name)
+	}
 	if rf, ok := ret.Get(0).(func(string) models.Device); ok {
 		r0 = rf(name)
 	} else {
 		r0 = ret.Get(0).(models.Device)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(name)
 	} else {
@@ -258,50 +310,21 @@ func (_m *DeviceServiceSDK) GetDeviceByName(name string) (models.Device, error)
 	return r0, r1
 }
 
-// GetLoggingClient provides a mock function with given fields:
-func (_m *DeviceServiceSDK) GetLoggingClient() logger.LoggingClient {
-	ret := _m.Called()
-
-	var r0 logger.LoggingClient
-	if rf, ok := ret.Get(0).(func() logger.LoggingClient); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(logger.LoggingClient)
-		}
-	}
-
-	return r0
-}
-
-// GetMetricsManager provides a mock function with given fields:
-func (_m *DeviceServiceSDK) GetMetricsManager() bootstrapinterfaces.MetricsManager {
-	ret := _m.Called()
-
-	var r0 bootstrapinterfaces.MetricsManager
-	if rf, ok := ret.Get(0).(func() bootstrapinterfaces.MetricsManager); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(bootstrapinterfaces.MetricsManager)
-		}
-	}
-
-	return r0
-}
-
 // GetProfileByName provides a mock function with given fields: name
 func (_m *DeviceServiceSDK) GetProfileByName(name string) (models.DeviceProfile, error) {
 	ret := _m.Called(name)
 
 	var r0 models.DeviceProfile
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (models.DeviceProfile, error)); ok {
+		return rf(name)
+	}
 	if rf, ok := ret.Get(0).(func(string) models.DeviceProfile); ok {
 		r0 = rf(name)
 	} else {
 		r0 = ret.Get(0).(models.DeviceProfile)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(name)
 	} else {
@@ -316,13 +339,16 @@ func (_m *DeviceServiceSDK) GetProvisionWatcherByName(name string) (models.Provi
 	ret := _m.Called(name)
 
 	var r0 models.ProvisionWatcher
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (models.ProvisionWatcher, error)); ok {
+		return rf(name)
+	}
 	if rf, ok := ret.Get(0).(func(string) models.ProvisionWatcher); ok {
 		r0 = rf(name)
 	} else {
 		r0 = ret.Get(0).(models.ProvisionWatcher)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(name)
 	} else {
@@ -332,22 +358,6 @@ func (_m *DeviceServiceSDK) GetProvisionWatcherByName(name string) (models.Provi
 	return r0, r1
 }
 
-// GetSecretProvider provides a mock function with given fields:
-func (_m *DeviceServiceSDK) GetSecretProvider() bootstrapinterfaces.SecretProvider {
-	ret := _m.Called()
-
-	var r0 bootstrapinterfaces.SecretProvider
-	if rf, ok := ret.Get(0).(func() bootstrapinterfaces.SecretProvider); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(bootstrapinterfaces.SecretProvider)
-		}
-	}
-
-	return r0
-}
-
 // ListenForCustomConfigChanges provides a mock function with given fields: configToWatch, sectionName, changedCallback
 func (_m *DeviceServiceSDK) ListenForCustomConfigChanges(configToWatch interface{}, sectionName string, changedCallback func(interface{})) error {
 	ret := _m.Called(configToWatch, sectionName, changedCallback)
@@ -363,11 +373,11 @@ func (_m *DeviceServiceSDK) ListenForCustomConfigChanges(configToWatch interface
 }
 
 // LoadCustomConfig provides a mock function with given fields: customConfig, sectionName
-func (_m *DeviceServiceSDK) LoadCustomConfig(customConfig service.UpdatableConfig, sectionName string) error {
+func (_m *DeviceServiceSDK) LoadCustomConfig(customConfig interfaces.UpdatableConfig, sectionName string) error {
 	ret := _m.Called(customConfig, sectionName)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(service.UpdatableConfig, string) error); ok {
+	if rf, ok := ret.Get(0).(func(interfaces.UpdatableConfig, string) error); ok {
 		r0 = rf(customConfig, sectionName)
 	} else {
 		r0 = ret.Error(0)
@@ -376,6 +386,38 @@ func (_m *DeviceServiceSDK) LoadCustomConfig(customConfig service.UpdatableConfi
 	return r0
 }
 
+// LoggingClient provides a mock function with given fields:
+func (_m *DeviceServiceSDK) LoggingClient() logger.LoggingClient {
+	ret := _m.Called()
+
+	var r0 logger.LoggingClient
+	if rf, ok := ret.Get(0).(func() logger.LoggingClient); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(logger.LoggingClient)
+		}
+	}
+
+	return r0
+}
+
+// MetricsManager provides a mock function with given fields:
+func (_m *DeviceServiceSDK) MetricsManager() bootstrapinterfaces.MetricsManager {
+	ret := _m.Called()
+
+	var r0 bootstrapinterfaces.MetricsManager
+	if rf, ok := ret.Get(0).(func() bootstrapinterfaces.MetricsManager); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(bootstrapinterfaces.MetricsManager)
+		}
+	}
+
+	return r0
+}
+
 // Name provides a mock function with given fields:
 func (_m *DeviceServiceSDK) Name() string {
 	ret := _m.Called()
@@ -462,6 +504,22 @@ func (_m *DeviceServiceSDK) RemoveProvisionWatcher(name string) error {
 	return r0
 }
 
+// SecretProvider provides a mock function with given fields:
+func (_m *DeviceServiceSDK) SecretProvider() bootstrapinterfaces.SecretProvider {
+	ret := _m.Called()
+
+	var r0 bootstrapinterfaces.SecretProvider
+	if rf, ok := ret.Get(0).(func() bootstrapinterfaces.SecretProvider); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(bootstrapinterfaces.SecretProvider)
+		}
+	}
+
+	return r0
+}
+
 // SetDeviceOpState provides a mock function with given fields: name, state
 func (_m *DeviceServiceSDK) SetDeviceOpState(name string, state models.OperatingState) error {
 	ret := _m.Called(name, state)
@@ -476,11 +534,6 @@ func (_m *DeviceServiceSDK) SetDeviceOpState(name string, state models.Operating
 	return r0
 }
 
-// Stop provides a mock function with given fields: force
-func (_m *DeviceServiceSDK) Stop(force bool) {
-	_m.Called(force)
-}
-
 // UpdateDevice provides a mock function with given fields: device
 func (_m *DeviceServiceSDK) UpdateDevice(device models.Device) error {
 	ret := _m.Called(device)
diff --git a/pkg/models/mocks/DeviceValidator.go b/pkg/interfaces/mocks/DeviceValidator.go
similarity index 95%
rename from pkg/models/mocks/DeviceValidator.go
rename to pkg/interfaces/mocks/DeviceValidator.go
index f6eaaae9..4a3d94ce 100644
--- a/pkg/models/mocks/DeviceValidator.go
+++ b/pkg/interfaces/mocks/DeviceValidator.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.20.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/pkg/models/mocks/ProtocolDriver.go b/pkg/interfaces/mocks/ProtocolDriver.go
similarity index 87%
rename from pkg/models/mocks/ProtocolDriver.go
rename to pkg/interfaces/mocks/ProtocolDriver.go
index 04745f00..23bc14c6 100644
--- a/pkg/models/mocks/ProtocolDriver.go
+++ b/pkg/interfaces/mocks/ProtocolDriver.go
@@ -1,9 +1,9 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.20.2. DO NOT EDIT.
 
 package mocks
 
 import (
-	logger "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
+	interfaces "github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces"
 	mock "github.com/stretchr/testify/mock"
 
 	models "github.com/edgexfoundry/go-mod-core-contracts/v3/models"
@@ -35,6 +35,10 @@ func (_m *ProtocolDriver) HandleReadCommands(deviceName string, protocols map[st
 	ret := _m.Called(deviceName, protocols, reqs)
 
 	var r0 []*pkgmodels.CommandValue
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, map[string]models.ProtocolProperties, []pkgmodels.CommandRequest) ([]*pkgmodels.CommandValue, error)); ok {
+		return rf(deviceName, protocols, reqs)
+	}
 	if rf, ok := ret.Get(0).(func(string, map[string]models.ProtocolProperties, []pkgmodels.CommandRequest) []*pkgmodels.CommandValue); ok {
 		r0 = rf(deviceName, protocols, reqs)
 	} else {
@@ -43,7 +47,6 @@ func (_m *ProtocolDriver) HandleReadCommands(deviceName string, protocols map[st
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string, map[string]models.ProtocolProperties, []pkgmodels.CommandRequest) error); ok {
 		r1 = rf(deviceName, protocols, reqs)
 	} else {
@@ -67,13 +70,13 @@ func (_m *ProtocolDriver) HandleWriteCommands(deviceName string, protocols map[s
 	return r0
 }
 
-// Initialize provides a mock function with given fields: lc, asyncCh, deviceCh
-func (_m *ProtocolDriver) Initialize(lc logger.LoggingClient, asyncCh chan<- *pkgmodels.AsyncValues, deviceCh chan<- []pkgmodels.DiscoveredDevice) error {
-	ret := _m.Called(lc, asyncCh, deviceCh)
+// Initialize provides a mock function with given fields: sdk
+func (_m *ProtocolDriver) Initialize(sdk interfaces.DeviceServiceSDK) error {
+	ret := _m.Called(sdk)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(logger.LoggingClient, chan<- *pkgmodels.AsyncValues, chan<- []pkgmodels.DiscoveredDevice) error); ok {
-		r0 = rf(lc, asyncCh, deviceCh)
+	if rf, ok := ret.Get(0).(func(interfaces.DeviceServiceSDK) error); ok {
+		r0 = rf(sdk)
 	} else {
 		r0 = ret.Error(0)
 	}
diff --git a/pkg/models/protocoldiscovery.go b/pkg/interfaces/protocoldiscovery.go
similarity index 67%
rename from pkg/models/protocoldiscovery.go
rename to pkg/interfaces/protocoldiscovery.go
index 6c2105ff..74ca3360 100644
--- a/pkg/models/protocoldiscovery.go
+++ b/pkg/interfaces/protocoldiscovery.go
@@ -5,11 +5,7 @@
 //
 // SPDX-License-Identifier: Apache-2.0
 
-package models
-
-import (
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
-)
+package interfaces
 
 // ProtocolDiscovery is a low-level device-specific interface implemented
 // by device services that support dynamic device discovery.
@@ -20,11 +16,3 @@ type ProtocolDiscovery interface {
 	// based on a set of acceptance criteria (i.e. Provision Watchers).
 	Discover()
 }
-
-// DiscoveredDevice defines the required information for a found device.
-type DiscoveredDevice struct {
-	Name        string
-	Protocols   map[string]models.ProtocolProperties
-	Description string
-	Labels      []string
-}
diff --git a/pkg/models/protocoldriver.go b/pkg/interfaces/protocoldriver.go
similarity index 79%
rename from pkg/models/protocoldriver.go
rename to pkg/interfaces/protocoldriver.go
index a29aaeba..59684db1 100644
--- a/pkg/models/protocoldriver.go
+++ b/pkg/interfaces/protocoldriver.go
@@ -1,40 +1,41 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
 // Copyright (C) 2018 Canonical Ltd
-// Copyright (C) 2018-2021 IOTech Ltd
+// Copyright (C) 2018-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
-// This package defines interfaces and structs used to build an EdgeX Foundry Device
-// Service.  The interfaces provide an asbstraction layer for the device
+// Package interfaces defines interfaces and structs used to build an EdgeX Foundry Device
+// Service.  The interfaces provide an abstraction layer for the device
 // or protocol specific logic of a Device Service, and the structs represents request
 // and response data format used by the protocol driver.
-package models
+package interfaces
 
 import (
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
+
+	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
 )
 
 // ProtocolDriver is a low-level device-specific interface used by
-// by other components of an EdgeX Device Service to interact with
+// other components of an EdgeX Device Service to interact with
 // a specific class of devices.
 type ProtocolDriver interface {
 	// Initialize performs protocol-specific initialization for the device service.
 	// The given *AsyncValues channel can be used to push asynchronous events and
 	// readings to Core Data. The given []DiscoveredDevice channel is used to send
 	// discovered devices that will be filtered and added to Core Metadata asynchronously.
-	Initialize(lc logger.LoggingClient, asyncCh chan<- *AsyncValues, deviceCh chan<- []DiscoveredDevice) error
+	Initialize(sdk DeviceServiceSDK) error
 
 	// HandleReadCommands passes a slice of CommandRequest struct each representing
 	// a ResourceOperation for a specific device resource.
-	HandleReadCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []CommandRequest) ([]*CommandValue, error)
+	HandleReadCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []sdkModels.CommandRequest) ([]*sdkModels.CommandValue, error)
 
 	// HandleWriteCommands passes a slice of CommandRequest struct each representing
 	// a ResourceOperation for a specific device resource.
 	// Since the commands are actuation commands, params provide parameters for the individual
 	// command.
-	HandleWriteCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []CommandRequest, params []*CommandValue) error
+	HandleWriteCommands(deviceName string, protocols map[string]models.ProtocolProperties, reqs []sdkModels.CommandRequest, params []*sdkModels.CommandValue) error
 
 	// Stop instructs the protocol-specific DS code to shutdown gracefully, or
 	// if the force parameter is 'true', immediately. The driver is responsible
diff --git a/pkg/interfaces/service.go b/pkg/interfaces/service.go
index effc474e..6bddb3b1 100644
--- a/pkg/interfaces/service.go
+++ b/pkg/interfaces/service.go
@@ -1,5 +1,6 @@
 //
 // Copyright (C) 2022 Intel Corporation
+// Copyright (C) 2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -12,88 +13,75 @@ import (
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
 
-	"github.com/edgexfoundry/device-sdk-go/v3/pkg/service"
+	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
 )
 
-// DeviceServiceSDK defines the interface for an Edgex Device Service SDK
-type DeviceServiceSDK interface {
-	// AddDeviceAutoEvent adds a new AutoEvent to the Device with given name
-	AddDeviceAutoEvent(deviceName string, event models.AutoEvent) error
-
-	// RemoveDeviceAutoEvent removes an AutoEvent from the Device with given name
-	RemoveDeviceAutoEvent(deviceName string, event models.AutoEvent) error
+// UpdatableConfig interface allows services to have custom configuration populated from configuration stored
+// in the Configuration Provider (aka Consul). Services using custom configuration must implement this interface
+// on their custom configuration, even if they do not use Configuration Provider. If they do not use the
+// Configuration Provider they can have a dummy implementation of this interface.
+// This wraps the actual interface from go-mod-bootstrap so device service code doesn't have to have the additional
+// direct import of go-mod-bootstrap.
+type UpdatableConfig interface {
+	interfaces.UpdatableConfig
+}
 
+// DeviceServiceSDK defines the interface for an EdgeX Device Service SDK
+type DeviceServiceSDK interface {
 	// AddDevice adds a new Device to the Device Service and Core Metadata
 	// Returns new Device id or non-nil error.
 	AddDevice(device models.Device) (string, error)
-
 	// Devices return all managed Devices from cache
 	Devices() []models.Device
-
 	// GetDeviceByName returns the Device by its name if it exists in the cache, or returns an error.
 	GetDeviceByName(name string) (models.Device, error)
-
-	//  DriverConfigs retrieves the driver specific configuration
-	DriverConfigs() map[string]string
-
-	// SetDeviceOpState sets the operating state of device
-	SetDeviceOpState(name string, state models.OperatingState) error
-
-	// RemoveDeviceByName removes the specified Device by name from the cache and ensures that the
-	// instance in Core Metadata is also removed.
-	RemoveDeviceByName(name string) error
-
 	// UpdateDevice updates the Device in the cache and ensures that the
 	// copy in Core Metadata is also updated.
 	UpdateDevice(device models.Device) error
-
-	// UpdateDeviceOperatingState updates the Device's OperatingState with given name
-	// in Core Metadata and device service cache.
-	UpdateDeviceOperatingState(deviceName string, state string) error
-
+	// RemoveDeviceByName removes the specified Device by name from the cache and ensures that the
+	// instance in Core Metadata is also removed.
+	RemoveDeviceByName(name string) error
 	// AddDeviceProfile adds a new DeviceProfile to the Device Service and Core Metadata
 	// Returns new DeviceProfile id or non-nil error.
 	AddDeviceProfile(profile models.DeviceProfile) (string, error)
-
 	// DeviceProfiles return all managed DeviceProfiles from cache
 	DeviceProfiles() []models.DeviceProfile
-
 	// GetProfileByName returns the Profile by its name if it exists in the cache, or returns an error.
 	GetProfileByName(name string) (models.DeviceProfile, error)
-
-	// RemoveDeviceProfileByName removes the specified DeviceProfile by name from the cache and ensures that the
-	// instance in Core Metadata is also removed.
-	RemoveDeviceProfileByName(name string) error
-
 	// UpdateDeviceProfile updates the DeviceProfile in the cache and ensures that the
 	// copy in Core Metadata is also updated.
 	UpdateDeviceProfile(profile models.DeviceProfile) error
-
-	// DeviceCommand retrieves the specific DeviceCommand instance from cache according to
-	// the Device name and Command name
-	DeviceCommand(deviceName string, commandName string) (models.DeviceCommand, bool)
-
-	// DeviceResource retrieves the specific DeviceResource instance from cache according to
-	// the Device name and Device Resource name
-	DeviceResource(deviceName string, deviceResource string) (models.DeviceResource, bool)
-
+	// RemoveDeviceProfileByName removes the specified DeviceProfile by name from the cache and ensures that the
+	// instance in Core Metadata is also removed.
+	RemoveDeviceProfileByName(name string) error
 	// AddProvisionWatcher adds a new Watcher to the cache and Core Metadata
 	// Returns new Watcher id or non-nil error.
 	AddProvisionWatcher(watcher models.ProvisionWatcher) (string, error)
-
 	// ProvisionWatchers return all managed Watchers from cache
 	ProvisionWatchers() []models.ProvisionWatcher
-
 	// GetProvisionWatcherByName returns the Watcher by its name if it exists in the cache, or returns an error.
 	GetProvisionWatcherByName(name string) (models.ProvisionWatcher, error)
-
-	// RemoveProvisionWatcher removes the specified Watcher by name from the cache and ensures that the
-	// instance in Core Metadata is also removed.
-	RemoveProvisionWatcher(name string) error
-
 	// UpdateProvisionWatcher updates the Watcher in the cache and ensures that the
 	// copy in Core Metadata is also updated.
 	UpdateProvisionWatcher(watcher models.ProvisionWatcher) error
+	// RemoveProvisionWatcher removes the specified Watcher by name from the cache and ensures that the
+	// instance in Core Metadata is also removed.
+	RemoveProvisionWatcher(name string) error
+	// DeviceResource retrieves the specific DeviceResource instance from cache according to
+	// the Device name and Device Resource name
+	DeviceResource(deviceName string, deviceResource string) (models.DeviceResource, bool)
+	// DeviceCommand retrieves the specific DeviceCommand instance from cache according to
+	// the Device name and Command name
+	DeviceCommand(deviceName string, commandName string) (models.DeviceCommand, bool)
+	// AddDeviceAutoEvent adds a new AutoEvent to the Device with given name
+	AddDeviceAutoEvent(deviceName string, event models.AutoEvent) error
+	// RemoveDeviceAutoEvent removes an AutoEvent from the Device with given name
+	RemoveDeviceAutoEvent(deviceName string, event models.AutoEvent) error
+	// SetDeviceOpState sets the operating state of device
+	SetDeviceOpState(name string, state models.OperatingState) error
+	// UpdateDeviceOperatingState updates the Device's OperatingState with given name
+	// in Core Metadata and device service cache.
+	UpdateDeviceOperatingState(deviceName string, state string) error
 
 	// Name returns the name of this Device Service
 	Name() string
@@ -101,43 +89,41 @@ type DeviceServiceSDK interface {
 	// Version returns the version number of this Device Service
 	Version() string
 
-	// AsyncReadings returns a bool value to indicate whether the asynchronous reading is enabled.
-	AsyncReadings() bool
+	// AsyncReadingsEnabled returns a bool value to indicate whether the asynchronous reading is enabled.
+	AsyncReadingsEnabled() bool
+
+	// AsyncValuesChannel returns a channel to allow developer send asynchronous reading back to SDK.
+	AsyncValuesChannel() chan *sdkModels.AsyncValues
 
-	// DeviceDiscovery returns a bool value to indicate whether device discovery is enabled.
-	DeviceDiscovery() bool
+	// DiscoveredDeviceChannel returns a channel to allow developer send discovered devices back to SDK.
+	DiscoveredDeviceChannel() chan []sdkModels.DiscoveredDevice
+
+	// DeviceDiscoveryEnabled returns a bool value to indicate whether device discovery is enabled.
+	DeviceDiscoveryEnabled() bool
+
+	// DriverConfigs retrieves the driver specific configuration
+	DriverConfigs() map[string]string
 
 	// AddRoute allows leveraging the existing internal web server to add routes specific to Device Service.
 	AddRoute(route string, handler func(http.ResponseWriter, *http.Request), methods ...string) error
 
-	// Stop shuts down the Service
-	Stop(force bool)
-
 	// LoadCustomConfig uses the Config Processor from go-mod-bootstrap to attempt to load service's
 	// custom configuration. It uses the same command line flags to process the custom config in the same manner
 	// as the standard configuration.
-	LoadCustomConfig(customConfig service.UpdatableConfig, sectionName string) error
+	LoadCustomConfig(customConfig UpdatableConfig, sectionName string) error
 
 	// ListenForCustomConfigChanges uses the Config Processor from go-mod-bootstrap to attempt to listen for
 	// changes to the specified custom configuration section. LoadCustomConfig must be called previously so that
 	// the instance of sdkService.configProcessor has already been set.
 	ListenForCustomConfigChanges(configToWatch interface{}, sectionName string, changedCallback func(interface{})) error
 
-	// GetLoggingClient returns the logger.LoggingClient. The name was chosen to avoid conflicts
-	// with service.DeviceService.LoggingClient struct field.
-	GetLoggingClient() logger.LoggingClient
+	// LoggingClient returns the logger.LoggingClient.
+	LoggingClient() logger.LoggingClient
 
-	// GetSecretProvider returns the interfaces.SecretProvider. The name was chosen to avoid conflicts
-	// with service.DeviceService.SecretProvider struct field.
-	GetSecretProvider() interfaces.SecretProvider
+	// SecretProvider returns the interfaces.SecretProvider.
+	SecretProvider() interfaces.SecretProvider
 
-	// GetMetricsManager returns the Metrics Manager used to register counter, gauge, gaugeFloat64 or timer metric types from
+	// MetricsManager returns the Metrics Manager used to register counter, gauge, gaugeFloat64 or timer metric types from
 	// github.com/rcrowley/go-metrics
-	GetMetricsManager() interfaces.MetricsManager
-}
-
-// Service returns the device service SDK instance as an interface
-// This provides the ability for device service's unit tests to mock the SDK calls allowing for more code coverage.
-func Service() DeviceServiceSDK {
-	return service.RunningService()
+	MetricsManager() interfaces.MetricsManager
 }
diff --git a/pkg/models/discovereddevice.go b/pkg/models/discovereddevice.go
new file mode 100644
index 00000000..c9d99709
--- /dev/null
+++ b/pkg/models/discovereddevice.go
@@ -0,0 +1,16 @@
+//
+// Copyright (C) 2023 IOTech Ltd
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package models
+
+import "github.com/edgexfoundry/go-mod-core-contracts/v3/models"
+
+// DiscoveredDevice defines the required information for a found device.
+type DiscoveredDevice struct {
+	Name        string
+	Protocols   map[string]models.ProtocolProperties
+	Description string
+	Labels      []string
+}
diff --git a/pkg/service/async.go b/pkg/service/async.go
index c86ed2de..88f61518 100644
--- a/pkg/service/async.go
+++ b/pkg/service/async.go
@@ -11,10 +11,9 @@ import (
 	"context"
 	"fmt"
 	"regexp"
-	"sync"
 
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
@@ -34,12 +33,7 @@ import (
 // is greater than or equal to two. Alternatively, we can process
 // AsyncValues one by one in the same order by changing the AsyncBufferSize
 // value to one.
-func (s *DeviceService) processAsyncResults(ctx context.Context, wg *sync.WaitGroup, dic *di.Container) {
-	wg.Add(1)
-	defer func() {
-		wg.Done()
-	}()
-
+func (s *deviceService) processAsyncResults(ctx context.Context, dic *di.Container) {
 	working := make(chan bool, s.config.Device.AsyncBufferSize)
 	for {
 		select {
@@ -52,18 +46,18 @@ func (s *DeviceService) processAsyncResults(ctx context.Context, wg *sync.WaitGr
 }
 
 // sendAsyncValues convert AsyncValues to event and send the event to CoreData
-func (s *DeviceService) sendAsyncValues(acv *sdkModels.AsyncValues, working chan bool, dic *di.Container) {
+func (s *deviceService) sendAsyncValues(acv *sdkModels.AsyncValues, working chan bool, dic *di.Container) {
 	working <- true
 	defer func() {
 		<-working
 	}()
 
 	if len(acv.CommandValues) == 0 {
-		s.LoggingClient.Error("Skip sending AsyncValues because the CommandValues is empty.")
+		s.lc.Error("Skip sending AsyncValues because the CommandValues is empty.")
 		return
 	}
 	if len(acv.CommandValues) > 1 && acv.SourceName == "" {
-		s.LoggingClient.Error("Skip sending AsyncValues because the SourceName is empty.")
+		s.lc.Error("Skip sending AsyncValues because the SourceName is empty.")
 		return
 	}
 	// We can use the first reading's DeviceResourceName as the SourceName
@@ -73,7 +67,7 @@ func (s *DeviceService) sendAsyncValues(acv *sdkModels.AsyncValues, working chan
 	}
 	event, err := transformer.CommandValuesToEventDTO(acv.CommandValues, acv.DeviceName, acv.SourceName, dic)
 	if err != nil {
-		s.LoggingClient.Errorf("failed to transform CommandValues to Event: %v", err)
+		s.lc.Errorf("failed to transform CommandValues to Event: %v", err)
 		return
 	}
 
@@ -82,11 +76,7 @@ func (s *DeviceService) sendAsyncValues(acv *sdkModels.AsyncValues, working chan
 
 // processAsyncFilterAndAdd filter and add devices discovered by
 // device service protocol discovery.
-func (s *DeviceService) processAsyncFilterAndAdd(ctx context.Context, wg *sync.WaitGroup) {
-	wg.Add(1)
-	defer func() {
-		wg.Done()
-	}()
+func (s *deviceService) processAsyncFilterAndAdd(ctx context.Context) {
 	for {
 		select {
 		case <-ctx.Done():
@@ -96,13 +86,13 @@ func (s *DeviceService) processAsyncFilterAndAdd(ctx context.Context, wg *sync.W
 			pws := cache.ProvisionWatchers().All()
 			for _, d := range devices {
 				for _, pw := range pws {
-					if checkAllowList(d, pw, s.LoggingClient) && checkBlockList(d, pw, s.LoggingClient) {
+					if s.checkAllowList(d, pw) && s.checkBlockList(d, pw) {
 						if _, ok := cache.Devices().ForName(d.Name); ok {
-							s.LoggingClient.Debugf("Candidate discovered device %s already existed", d.Name)
+							s.lc.Debugf("Candidate discovered device %s already existed", d.Name)
 							break
 						}
 
-						s.LoggingClient.Infof("Adding discovered device %s to Metadata", d.Name)
+						s.lc.Infof("Adding discovered device %s to Metadata", d.Name)
 						device := models.Device{
 							Name:           d.Name,
 							Description:    d.Description,
@@ -116,21 +106,21 @@ func (s *DeviceService) processAsyncFilterAndAdd(ctx context.Context, wg *sync.W
 						}
 
 						req := requests.NewAddDeviceRequest(dtos.FromDeviceModelToDTO(device))
-						_, err := s.edgexClients.DeviceClient.Add(ctx, []requests.AddDeviceRequest{req})
+						_, err := container.DeviceClientFrom(s.dic.Get).Add(ctx, []requests.AddDeviceRequest{req})
 						if err != nil {
-							s.LoggingClient.Errorf("failed to create discovered device %s: %v", device.Name, err)
+							s.lc.Errorf("failed to create discovered device %s: %v", device.Name, err)
 						} else {
 							break
 						}
 					}
 				}
 			}
-			s.LoggingClient.Debug("Filtered device addition finished")
+			s.lc.Debug("Filtered device addition finished")
 		}
 	}
 }
 
-func checkAllowList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher, lc logger.LoggingClient) bool {
+func (s *deviceService) checkAllowList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher) bool {
 	// ignore the device protocol properties name
 	for _, protocol := range d.Protocols {
 		matchedCount := 0
@@ -138,12 +128,12 @@ func checkAllowList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher, lc
 			if value, ok := protocol[name]; ok {
 				valueString := fmt.Sprintf("%v", value)
 				if valueString == "" {
-					lc.Debugf("Skipping identifier %s, cannot transform %s value '%v' to string type for discovered device %s", name, name, value, d.Name)
+					s.lc.Debugf("Skipping identifier %s, cannot transform %s value '%v' to string type for discovered device %s", name, name, value, d.Name)
 					continue
 				}
 				matched, err := regexp.MatchString(regex, valueString)
 				if !matched || err != nil {
-					lc.Debugf("Discovered Device %s %s value '%v' did not match PW identifier: %s", d.Name, name, value, regex)
+					s.lc.Debugf("Discovered Device %s %s value '%v' did not match PW identifier: %s", d.Name, name, value, regex)
 					break
 				}
 				matchedCount += 1
@@ -157,7 +147,7 @@ func checkAllowList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher, lc
 	return false
 }
 
-func checkBlockList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher, lc logger.LoggingClient) bool {
+func (s *deviceService) checkBlockList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher) bool {
 	// a candidate should match none of the blocking identifiers
 	for name, blacklist := range pw.BlockingIdentifiers {
 		// ignore the device protocol properties name
@@ -165,12 +155,12 @@ func checkBlockList(d sdkModels.DiscoveredDevice, pw models.ProvisionWatcher, lc
 			if value, ok := protocol[name]; ok {
 				valueString := fmt.Sprintf("%v", value)
 				if valueString == "" {
-					lc.Debugf("Skipping identifier %s, cannot transform %s value '%v' to string type for discovered device %s", name, name, value, d.Name)
+					s.lc.Debugf("Skipping identifier %s, cannot transform %s value '%v' to string type for discovered device %s", name, name, value, d.Name)
 					continue
 				}
 				for _, v := range blacklist {
 					if valueString == v {
-						lc.Debugf("Discovered Device %s %s value cannot be %v", d.Name, name, value)
+						s.lc.Debugf("Discovered Device %s %s value cannot be %v", d.Name, name, value)
 						return false
 					}
 				}
diff --git a/pkg/service/init.go b/pkg/service/init.go
index ca363c17..6f66cc7c 100644
--- a/pkg/service/init.go
+++ b/pkg/service/init.go
@@ -10,86 +10,129 @@ import (
 	"context"
 	"sync"
 
+	bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers"
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup"
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
 	"github.com/gorilla/mux"
 
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/common"
-
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/cache"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/common"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/controller/http"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/controller/messaging"
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/provision"
 	"github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
 )
 
 // Bootstrap contains references to dependencies required by the BootstrapHandler.
 type Bootstrap struct {
-	router *mux.Router
+	deviceService *deviceService
+	router        *mux.Router
 }
 
 // NewBootstrap is a factory method that returns an initialized Bootstrap receiver struct.
-func NewBootstrap(router *mux.Router) *Bootstrap {
+func NewBootstrap(ds *deviceService, router *mux.Router) *Bootstrap {
 	return &Bootstrap{
-		router: router,
+		deviceService: ds,
+		router:        router,
 	}
 }
 
-func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startupTimer startup.Timer, dic *di.Container) (success bool) {
-	ds.UpdateFromContainer(b.router, dic)
-	ds.ctx = ctx
-	ds.wg = wg
-	ds.controller.InitRestRoutes()
+func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) (success bool) {
+	s := b.deviceService
+	s.wg = wg
+	s.ctx = ctx
+	s.lc = bootstrapContainer.LoggingClientFrom(dic.Get)
+	s.autoEventManager = container.AutoEventManagerFrom(dic.Get)
+	s.controller = http.NewRestController(b.router, dic, s.serviceKey)
+	s.controller.InitRestRoutes()
+
+	edgexErr := cache.InitCache(s.serviceKey, dic)
+	if edgexErr != nil {
+		s.lc.Errorf("Failed to init cache: %s", edgexErr.Error())
+		return false
+	}
+
+	if s.AsyncReadingsEnabled() {
+		s.asyncCh = make(chan *models.AsyncValues, s.config.Device.AsyncBufferSize)
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			s.processAsyncResults(ctx, dic)
+		}()
+	}
+
+	if s.DeviceDiscoveryEnabled() {
+		s.deviceCh = make(chan []models.DiscoveredDevice, 1)
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			s.processAsyncFilterAndAdd(ctx)
+		}()
+	}
 
-	err := cache.InitCache(ds.ServiceName, dic)
+	err := s.driver.Initialize(s)
 	if err != nil {
-		ds.LoggingClient.Errorf("Failed to init cache: %v", err)
+		s.lc.Errorf("ProtocolDriver init failed: %s", err.Error())
+		return false
+	}
+
+	edgexErr = s.selfRegister()
+	if edgexErr != nil {
+		s.lc.Errorf("Failed to register %s on Metadata: %s", s.serviceKey, edgexErr.Error())
 		return false
 	}
 
-	if ds.AsyncReadings() {
-		ds.asyncCh = make(chan *models.AsyncValues, ds.config.Device.AsyncBufferSize)
-		go ds.processAsyncResults(ctx, wg, dic)
+	edgexErr = provision.LoadProfiles(s.config.Device.ProfilesDir, dic)
+	if edgexErr != nil {
+		s.lc.Errorf("Failed to load device profiles: %s", edgexErr.Error())
+		return false
 	}
-	if ds.DeviceDiscovery() {
-		ds.deviceCh = make(chan []models.DiscoveredDevice, 1)
-		go ds.processAsyncFilterAndAdd(ctx, wg)
+
+	edgexErr = provision.LoadDevices(s.config.Device.DevicesDir, dic)
+	if edgexErr != nil {
+		s.lc.Errorf("Failed to load devices: %s", edgexErr.Error())
+		return false
 	}
 
-	e := ds.driver.Initialize(ds.LoggingClient, ds.asyncCh, ds.deviceCh)
-	if e != nil {
-		ds.LoggingClient.Errorf("Failed to init ProtocolDriver: %v", e)
+	edgexErr = provision.LoadProvisionWatchers(s.config.Device.ProvisionWatchersDir, dic)
+	if edgexErr != nil {
+		s.lc.Errorf("Failed to load provision watchers: %s", edgexErr.Error())
 		return false
 	}
-	ds.initialized = true
 
-	err = ds.selfRegister()
-	if err != nil {
-		ds.LoggingClient.Errorf("Failed to register service on Metadata: %v", err)
+	s.autoEventManager.StartAutoEvents()
+
+	// Very important that this bootstrap handler is called after the NewServiceMetrics handler so
+	// MetricsManager dependency has been created.
+	common.InitializeSentMetrics(s.lc, dic)
+	return true
+}
+
+func messageBusBootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startupTimer startup.Timer, dic *di.Container) bool {
+	if !handlers.MessagingBootstrapHandler(ctx, wg, startupTimer, dic) {
 		return false
 	}
 
-	err = provision.LoadProfiles(ds.config.Device.ProfilesDir, dic)
+	lc := bootstrapContainer.LoggingClientFrom(dic.Get)
+	err := messaging.SubscribeCommands(ctx, dic)
 	if err != nil {
-		ds.LoggingClient.Errorf("Failed to create the pre-defined device profiles: %v", err)
+		lc.Errorf("Failed to subscribe internal command request: %v", err)
 		return false
 	}
 
-	err = provision.LoadDevices(ds.config.Device.DevicesDir, dic)
+	err = messaging.MetadataSystemEventsCallback(ctx, dic)
 	if err != nil {
-		ds.LoggingClient.Errorf("Failed to create the pre-defined devices: %v", err)
+		lc.Errorf("Failed to subscribe Metadata system events: %v", err)
 		return false
 	}
 
-	err = provision.LoadProvisionWatchers(ds.config.Device.ProvisionWatchersDir, dic)
+	err = messaging.SubscribeDeviceValidation(ctx, dic)
 	if err != nil {
-		ds.LoggingClient.Errorf("Failed to create the pre-defined provision watchers: %v", err)
+		lc.Errorf("Failed to subscribe device validation request: %v", err)
 		return false
 	}
 
-	ds.manager.StartAutoEvents()
-
-	// Very important that this handler is called after the NewServiceMetrics handler so
-	// MetricsManager dependency has been created.
-	common.InitializeSentMetrics(ds.LoggingClient, dic)
-
 	return true
 }
diff --git a/pkg/service/main.go b/pkg/service/main.go
deleted file mode 100644
index 5c33ac5b..00000000
--- a/pkg/service/main.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// -*- Mode: Go; indent-tabs-mode: t -*-
-//
-// Copyright (C) 2020-2023 IOTech Ltd
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package service
-
-import (
-	"context"
-	"os"
-	"sync"
-
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap"
-	bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/flags"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/config"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
-	"github.com/gorilla/mux"
-
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/autodiscovery"
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/autoevent"
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/controller/messaging"
-)
-
-const EnvInstanceName = "EDGEX_INSTANCE_NAME"
-
-var instanceName string
-
-func Main(serviceName string, serviceVersion string, proto interface{}, ctx context.Context, cancel context.CancelFunc, router *mux.Router) {
-	startupTimer := startup.NewStartUpTimer(serviceName)
-
-	additionalUsage :=
-		"    -i, --instance                  Provides a service name suffix which allows unique instance to be created\n" +
-			"                                    If the option is provided, service name will be replaced with \"<name>_<instance>\"\n"
-	sdkFlags := flags.NewWithUsage(additionalUsage)
-	sdkFlags.FlagSet.StringVar(&instanceName, "instance", "", "")
-	sdkFlags.FlagSet.StringVar(&instanceName, "i", "", "")
-	sdkFlags.Parse(os.Args[1:])
-
-	serviceName = setServiceName(serviceName)
-	ds = &DeviceService{}
-	ds.Initialize(serviceName, serviceVersion, proto)
-
-	ds.flags = sdkFlags
-
-	ds.dic = di.NewContainer(di.ServiceConstructorMap{
-		container.ConfigurationName: func(get di.Get) interface{} {
-			return ds.config
-		},
-		container.DeviceServiceName: func(get di.Get) interface{} {
-			return ds.deviceService
-		},
-		container.ProtocolDriverName: func(get di.Get) interface{} {
-			return ds.driver
-		},
-		container.ProtocolDiscoveryName: func(get di.Get) interface{} {
-			return ds.discovery
-		},
-		container.DeviceValidatorName: func(get di.Get) interface{} {
-			return ds.validator
-		},
-	})
-
-	httpServer := handlers.NewHttpServer(router, true)
-
-	bootstrap.Run(
-		ctx,
-		cancel,
-		sdkFlags,
-		ds.ServiceName,
-		common.ConfigStemDevice,
-		ds.config,
-		startupTimer,
-		ds.dic,
-		true,
-		config.ServiceTypeDevice,
-		[]interfaces.BootstrapHandler{
-			httpServer.BootstrapHandler,
-			messageBusBootstrapHandler,
-			handlers.NewServiceMetrics(ds.ServiceName).BootstrapHandler, // Must be after Messaging
-			handlers.NewClientsBootstrap().BootstrapHandler,
-			autoevent.BootstrapHandler,
-			NewBootstrap(router).BootstrapHandler,
-			autodiscovery.BootstrapHandler,
-			handlers.NewStartMessage(serviceName, serviceVersion).BootstrapHandler,
-		})
-
-	ds.Stop(false)
-}
-
-func messageBusBootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startupTimer startup.Timer, dic *di.Container) bool {
-	if !handlers.MessagingBootstrapHandler(ctx, wg, startupTimer, dic) {
-		return false
-	}
-
-	lc := bootstrapContainer.LoggingClientFrom(dic.Get)
-	err := messaging.SubscribeCommands(ctx, dic)
-	if err != nil {
-		lc.Errorf("Failed to subscribe internal command request: %v", err)
-		return false
-	}
-
-	err = messaging.MetadataSystemEventCallback(ctx, dic)
-	if err != nil {
-		lc.Errorf("Failed to subscribe Metadata system event: %v", err)
-		return false
-	}
-
-	err = messaging.SubscribeDeviceValidation(ctx, dic)
-	if err != nil {
-		lc.Errorf("Failed to subscribe device validation request: %v", err)
-		return false
-	}
-
-	return true
-}
-
-func setServiceName(name string) string {
-	envValue := os.Getenv(EnvInstanceName)
-	if len(envValue) > 0 {
-		instanceName = envValue
-	}
-
-	if len(instanceName) > 0 {
-		name = name + "_" + instanceName
-	}
-
-	return name
-}
diff --git a/pkg/service/managedautoevents.go b/pkg/service/managedautoevents.go
index 22eafde8..303aba59 100644
--- a/pkg/service/managedautoevents.go
+++ b/pkg/service/managedautoevents.go
@@ -1,7 +1,7 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
 // Copyright (C) 2020 VMware
-// Copyright (C) 2021 IOTech Ltd
+// Copyright (C) 2021-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -17,18 +17,18 @@ import (
 )
 
 // AddDeviceAutoEvent adds a new AutoEvent to the Device with given name
-func (s *DeviceService) AddDeviceAutoEvent(deviceName string, event models.AutoEvent) error {
+func (s *deviceService) AddDeviceAutoEvent(deviceName string, event models.AutoEvent) error {
 	found := false
 	device, ok := cache.Devices().ForName(deviceName)
 	if !ok {
 		msg := fmt.Sprintf("failed to find device %s in cache", deviceName)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
 	for _, e := range device.AutoEvents {
 		if e.SourceName == event.SourceName {
-			s.LoggingClient.Debugf("Updating existing AutoEvent %s for device %s", e.SourceName, deviceName)
+			s.lc.Debugf("Updating existing AutoEvent %s for device %s", e.SourceName, deviceName)
 			e.Interval = event.Interval
 			e.OnChange = event.OnChange
 			found = true
@@ -37,31 +37,31 @@ func (s *DeviceService) AddDeviceAutoEvent(deviceName string, event models.AutoE
 	}
 
 	if !found {
-		s.LoggingClient.Debugf("Adding new AutoEvent %s to device %s", event.SourceName, deviceName)
+		s.lc.Debugf("Adding new AutoEvent %s to device %s", event.SourceName, deviceName)
 		device.AutoEvents = append(device.AutoEvents, event)
 		err := cache.Devices().Update(device)
 		if err != nil {
-			s.LoggingClient.Errorf("failed to update device %s with AutoEvent change", deviceName)
+			s.lc.Errorf("failed to update device %s with AutoEvent change", deviceName)
 			return err
 		}
 	}
-	s.manager.RestartForDevice(deviceName)
+	s.autoEventManager.RestartForDevice(deviceName)
 
 	return nil
 }
 
 // RemoveDeviceAutoEvent removes an AutoEvent from the Device with given name
-func (s *DeviceService) RemoveDeviceAutoEvent(deviceName string, event models.AutoEvent) error {
+func (s *deviceService) RemoveDeviceAutoEvent(deviceName string, event models.AutoEvent) error {
 	device, ok := cache.Devices().ForName(deviceName)
 	if !ok {
 		msg := fmt.Sprintf("failed to find device %s cannot in cache", deviceName)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
 	for i, e := range device.AutoEvents {
 		if e.SourceName == event.SourceName {
-			s.LoggingClient.Debugf("Removing AutoEvent %s for device %s", e.SourceName, deviceName)
+			s.lc.Debugf("Removing AutoEvent %s for device %s", e.SourceName, deviceName)
 			device.AutoEvents = append(device.AutoEvents[:i], device.AutoEvents[i+1:]...)
 			break
 		}
@@ -69,10 +69,10 @@ func (s *DeviceService) RemoveDeviceAutoEvent(deviceName string, event models.Au
 
 	err := cache.Devices().Update(device)
 	if err != nil {
-		s.LoggingClient.Errorf("failed to update device %s with AutoEvent change", deviceName)
+		s.lc.Errorf("failed to update device %s with AutoEvent change", deviceName)
 		return err
 	}
-	s.manager.RestartForDevice(deviceName)
+	s.autoEventManager.RestartForDevice(deviceName)
 
 	return nil
 }
diff --git a/pkg/service/manageddevices.go b/pkg/service/manageddevices.go
index 9ad85a04..113fec0c 100644
--- a/pkg/service/manageddevices.go
+++ b/pkg/service/manageddevices.go
@@ -1,7 +1,7 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
 // Copyright (C) 2017-2018 Canonical Ltd
-// Copyright (C) 2018-2021 IOTech Ltd
+// Copyright (C) 2018-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -11,6 +11,7 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
 	commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common"
@@ -24,19 +25,19 @@ import (
 
 // AddDevice adds a new Device to the Device Service and Core Metadata
 // Returns new Device id or non-nil error.
-func (s *DeviceService) AddDevice(device models.Device) (string, error) {
+func (s *deviceService) AddDevice(device models.Device) (string, error) {
 	if d, ok := cache.Devices().ForName(device.Name); ok {
 		return d.Id, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("name conflicted, Device %s exists", device.Name), nil)
 	}
 
-	device.ServiceName = s.ServiceName
+	device.ServiceName = s.serviceKey
 
-	s.LoggingClient.Debugf("Adding managed Device %s", device.Name)
+	s.lc.Debugf("Adding managed Device %s", device.Name)
 	req := requests.NewAddDeviceRequest(dtos.FromDeviceModelToDTO(device))
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	res, err := s.edgexClients.DeviceClient.Add(ctx, []requests.AddDeviceRequest{req})
+	res, err := container.DeviceClientFrom(s.dic.Get).Add(ctx, []requests.AddDeviceRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to add Device %s to Core Metadata: %v", device.Name, err)
+		s.lc.Errorf("failed to add Device %s to Core Metadata: %v", device.Name, err)
 		return "", err
 	}
 
@@ -44,16 +45,16 @@ func (s *DeviceService) AddDevice(device models.Device) (string, error) {
 }
 
 // Devices return all managed Devices from cache
-func (s *DeviceService) Devices() []models.Device {
+func (s *deviceService) Devices() []models.Device {
 	return cache.Devices().All()
 }
 
 // GetDeviceByName returns the Device by its name if it exists in the cache, or returns an error.
-func (s *DeviceService) GetDeviceByName(name string) (models.Device, error) {
+func (s *deviceService) GetDeviceByName(name string) (models.Device, error) {
 	device, ok := cache.Devices().ForName(name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find Device %s in cache", name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return models.Device{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 	return device, nil
@@ -61,19 +62,19 @@ func (s *DeviceService) GetDeviceByName(name string) (models.Device, error) {
 
 // RemoveDeviceByName removes the specified Device by name from the cache and ensures that the
 // instance in Core Metadata is also removed.
-func (s *DeviceService) RemoveDeviceByName(name string) error {
+func (s *deviceService) RemoveDeviceByName(name string) error {
 	device, ok := cache.Devices().ForName(name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find device %s in cache", name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Removing managed Device %s", device.Name)
+	s.lc.Debugf("Removing managed Device %s", device.Name)
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.DeviceClient.DeleteDeviceByName(ctx, name)
+	_, err := container.DeviceClientFrom(s.dic.Get).DeleteDeviceByName(ctx, name)
 	if err != nil {
-		s.LoggingClient.Errorf("failed to delete Device %s in Core Metadata", name)
+		s.lc.Errorf("failed to delete Device %s in Core Metadata", name)
 	}
 
 	return err
@@ -81,21 +82,21 @@ func (s *DeviceService) RemoveDeviceByName(name string) error {
 
 // UpdateDevice updates the Device in the cache and ensures that the
 // copy in Core Metadata is also updated.
-func (s *DeviceService) UpdateDevice(device models.Device) error {
+func (s *deviceService) UpdateDevice(device models.Device) error {
 	_, ok := cache.Devices().ForName(device.Name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find Device %s in cache", device.Name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Updating managed Device %s", device.Name)
+	s.lc.Debugf("Updating managed Device %s", device.Name)
 	req := requests.NewUpdateDeviceRequest(dtos.FromDeviceModelToUpdateDTO(device))
 	req.Device.Id = nil
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.DeviceClient.Update(ctx, []requests.UpdateDeviceRequest{req})
+	_, err := container.DeviceClientFrom(s.dic.Get).Update(ctx, []requests.UpdateDeviceRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to update Device %s in Core Metadata: %v", device.Name, err)
+		s.lc.Errorf("failed to update Device %s in Core Metadata: %v", device.Name, err)
 	}
 
 	return err
@@ -103,15 +104,15 @@ func (s *DeviceService) UpdateDevice(device models.Device) error {
 
 // UpdateDeviceOperatingState updates the Device's OperatingState with given name
 // in Core Metadata and device service cache.
-func (s *DeviceService) UpdateDeviceOperatingState(deviceName string, state string) error {
+func (s *deviceService) UpdateDeviceOperatingState(deviceName string, state string) error {
 	d, ok := cache.Devices().ForName(deviceName)
 	if !ok {
 		msg := fmt.Sprintf("failed to find Device %s in cache", deviceName)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Updating managed Device OperatingState %s", d.Name)
+	s.lc.Debugf("Updating managed Device OperatingState %s", d.Name)
 	req := requests.UpdateDeviceRequest{
 		BaseRequest: commonDTO.NewBaseRequest(),
 		Device: dtos.UpdateDevice{
@@ -120,10 +121,21 @@ func (s *DeviceService) UpdateDeviceOperatingState(deviceName string, state stri
 		},
 	}
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.DeviceClient.Update(ctx, []requests.UpdateDeviceRequest{req})
+	_, err := container.DeviceClientFrom(s.dic.Get).Update(ctx, []requests.UpdateDeviceRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to update Device %s OperatingState in Core Metadata: %v", d.Name, err)
+		s.lc.Errorf("failed to update Device %s OperatingState in Core Metadata: %v", d.Name, err)
 	}
 
 	return err
 }
+
+// SetDeviceOpState sets the operating state of device
+func (s *deviceService) SetDeviceOpState(name string, state models.OperatingState) error {
+	d, err := s.GetDeviceByName(name)
+	if err != nil {
+		return err
+	}
+
+	d.OperatingState = state
+	return s.UpdateDevice(d)
+}
diff --git a/pkg/service/managedprofiles.go b/pkg/service/managedprofiles.go
index 03b7536c..7c8e3a60 100644
--- a/pkg/service/managedprofiles.go
+++ b/pkg/service/managedprofiles.go
@@ -1,7 +1,7 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
 // Copyright (C) 2017-2018 Canonical Ltd
-// Copyright (C) 2018-2021 IOTech Ltd
+// Copyright (C) 2018-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -11,6 +11,7 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests"
@@ -23,22 +24,22 @@ import (
 
 // AddDeviceProfile adds a new DeviceProfile to the Device Service and Core Metadata
 // Returns new DeviceProfile id or non-nil error.
-func (s *DeviceService) AddDeviceProfile(profile models.DeviceProfile) (string, error) {
+func (s *deviceService) AddDeviceProfile(profile models.DeviceProfile) (string, error) {
 	if p, ok := cache.Profiles().ForName(profile.Name); ok {
 		return p.Id, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("name conflicted, Profile %s exists", profile.Name), nil)
 	}
 
-	s.LoggingClient.Debugf("Adding managed Profile %s", profile.Name)
+	s.lc.Debugf("Adding managed Profile %s", profile.Name)
 	req := requests.NewDeviceProfileRequest(dtos.FromDeviceProfileModelToDTO(profile))
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	res, err := s.edgexClients.DeviceProfileClient.Add(ctx, []requests.DeviceProfileRequest{req})
+	res, err := container.DeviceProfileClientFrom(s.dic.Get).Add(ctx, []requests.DeviceProfileRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to add Profile %s to Core Metadata: %v", profile.Name, err)
+		s.lc.Errorf("failed to add Profile %s to Core Metadata: %v", profile.Name, err)
 		return "", err
 	}
 	err = cache.Profiles().Add(profile)
 	if err != nil {
-		s.LoggingClient.Errorf("failed to add Profile %s to cache", profile.Name)
+		s.lc.Errorf("failed to add Profile %s to cache", profile.Name)
 		return "", err
 	}
 
@@ -46,16 +47,16 @@ func (s *DeviceService) AddDeviceProfile(profile models.DeviceProfile) (string,
 }
 
 // DeviceProfiles return all managed DeviceProfiles from cache
-func (s *DeviceService) DeviceProfiles() []models.DeviceProfile {
+func (s *deviceService) DeviceProfiles() []models.DeviceProfile {
 	return cache.Profiles().All()
 }
 
 // GetProfileByName returns the Profile by its name if it exists in the cache, or returns an error.
-func (s *DeviceService) GetProfileByName(name string) (models.DeviceProfile, error) {
+func (s *deviceService) GetProfileByName(name string) (models.DeviceProfile, error) {
 	profile, ok := cache.Profiles().ForName(name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find Profile %s in cache", name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return models.DeviceProfile{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 	return profile, nil
@@ -63,19 +64,19 @@ func (s *DeviceService) GetProfileByName(name string) (models.DeviceProfile, err
 
 // RemoveDeviceProfileByName removes the specified DeviceProfile by name from the cache and ensures that the
 // instance in Core Metadata is also removed.
-func (s *DeviceService) RemoveDeviceProfileByName(name string) error {
+func (s *deviceService) RemoveDeviceProfileByName(name string) error {
 	profile, ok := cache.Profiles().ForName(name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find Profile %s in cache", name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Removing managed Profile %s", profile.Name)
+	s.lc.Debugf("Removing managed Profile %s", profile.Name)
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.DeviceProfileClient.DeleteByName(ctx, name)
+	_, err := container.DeviceProfileClientFrom(s.dic.Get).DeleteByName(ctx, name)
 	if err != nil {
-		s.LoggingClient.Errorf("failed to delete Profile %s in Core Metadata", name)
+		s.lc.Errorf("failed to delete Profile %s in Core Metadata", name)
 		return err
 	}
 
@@ -85,20 +86,20 @@ func (s *DeviceService) RemoveDeviceProfileByName(name string) error {
 
 // UpdateDeviceProfile updates the DeviceProfile in the cache and ensures that the
 // copy in Core Metadata is also updated.
-func (s *DeviceService) UpdateDeviceProfile(profile models.DeviceProfile) error {
+func (s *deviceService) UpdateDeviceProfile(profile models.DeviceProfile) error {
 	_, ok := cache.Profiles().ForName(profile.Name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find Profile %s in cache", profile.Name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Updating managed Profile %s", profile.Name)
+	s.lc.Debugf("Updating managed Profile %s", profile.Name)
 	req := requests.NewDeviceProfileRequest(dtos.FromDeviceProfileModelToDTO(profile))
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.DeviceProfileClient.Update(ctx, []requests.DeviceProfileRequest{req})
+	_, err := container.DeviceProfileClientFrom(s.dic.Get).Update(ctx, []requests.DeviceProfileRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to update Profile %s in Core Metadata: %v", profile.Name, err)
+		s.lc.Errorf("failed to update Profile %s in Core Metadata: %v", profile.Name, err)
 		return err
 	}
 
@@ -107,10 +108,10 @@ func (s *DeviceService) UpdateDeviceProfile(profile models.DeviceProfile) error
 
 // DeviceCommand retrieves the specific DeviceCommand instance from cache according to
 // the Device name and Command name
-func (s *DeviceService) DeviceCommand(deviceName string, commandName string) (models.DeviceCommand, bool) {
+func (s *deviceService) DeviceCommand(deviceName string, commandName string) (models.DeviceCommand, bool) {
 	device, ok := cache.Devices().ForName(deviceName)
 	if !ok {
-		s.LoggingClient.Errorf("failed to find device %s in cache", deviceName)
+		s.lc.Errorf("failed to find device %s in cache", deviceName)
 		return models.DeviceCommand{}, false
 	}
 
@@ -123,10 +124,10 @@ func (s *DeviceService) DeviceCommand(deviceName string, commandName string) (mo
 
 // DeviceResource retrieves the specific DeviceResource instance from cache according to
 // the Device name and Device Resource name
-func (s *DeviceService) DeviceResource(deviceName string, deviceResource string) (models.DeviceResource, bool) {
+func (s *deviceService) DeviceResource(deviceName string, deviceResource string) (models.DeviceResource, bool) {
 	device, ok := cache.Devices().ForName(deviceName)
 	if !ok {
-		s.LoggingClient.Errorf("failed to find device %s in cache", deviceName)
+		s.lc.Errorf("failed to find device %s in cache", deviceName)
 		return models.DeviceResource{}, false
 	}
 
diff --git a/pkg/service/managedwatchers.go b/pkg/service/managedwatchers.go
index bb0329e3..d1fa6422 100644
--- a/pkg/service/managedwatchers.go
+++ b/pkg/service/managedwatchers.go
@@ -1,6 +1,6 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
-// Copyright (C) 2020-2021 IOTech Ltd
+// Copyright (C) 2020-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -10,6 +10,7 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests"
@@ -22,20 +23,20 @@ import (
 
 // AddProvisionWatcher adds a new Watcher to the cache and Core Metadata
 // Returns new Watcher id or non-nil error.
-func (s *DeviceService) AddProvisionWatcher(watcher models.ProvisionWatcher) (string, error) {
+func (s *deviceService) AddProvisionWatcher(watcher models.ProvisionWatcher) (string, error) {
 	if pw, ok := cache.ProvisionWatchers().ForName(watcher.Name); ok {
 		return pw.Id,
 			errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("name conflicted, ProvisionWatcher %s exists", watcher.Name), nil)
 	}
 
-	watcher.ServiceName = s.ServiceName
+	watcher.ServiceName = s.serviceKey
 
-	s.LoggingClient.Debugf("Adding managed ProvisionWatcher %s", watcher.Name)
+	s.lc.Debugf("Adding managed ProvisionWatcher %s", watcher.Name)
 	req := requests.NewAddProvisionWatcherRequest(dtos.FromProvisionWatcherModelToDTO(watcher))
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	res, err := s.edgexClients.ProvisionWatcherClient.Add(ctx, []requests.AddProvisionWatcherRequest{req})
+	res, err := container.ProvisionWatcherClientFrom(s.dic.Get).Add(ctx, []requests.AddProvisionWatcherRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to add ProvisionWatcher to Core Metadata: %v", watcher.Name, err)
+		s.lc.Errorf("failed to add ProvisionWatcher to Core Metadata: %v", watcher.Name, err)
 		return "", err
 	}
 
@@ -43,16 +44,16 @@ func (s *DeviceService) AddProvisionWatcher(watcher models.ProvisionWatcher) (st
 }
 
 // ProvisionWatchers return all managed Watchers from cache
-func (s *DeviceService) ProvisionWatchers() []models.ProvisionWatcher {
+func (s *deviceService) ProvisionWatchers() []models.ProvisionWatcher {
 	return cache.ProvisionWatchers().All()
 }
 
 // GetProvisionWatcherByName returns the Watcher by its name if it exists in the cache, or returns an error.
-func (s *DeviceService) GetProvisionWatcherByName(name string) (models.ProvisionWatcher, error) {
+func (s *deviceService) GetProvisionWatcherByName(name string) (models.ProvisionWatcher, error) {
 	pw, ok := cache.ProvisionWatchers().ForName(name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find ProvisionWatcher %s in cache", name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return models.ProvisionWatcher{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 	return pw, nil
@@ -60,19 +61,19 @@ func (s *DeviceService) GetProvisionWatcherByName(name string) (models.Provision
 
 // RemoveProvisionWatcher removes the specified Watcher by name from the cache and ensures that the
 // instance in Core Metadata is also removed.
-func (s *DeviceService) RemoveProvisionWatcher(name string) error {
+func (s *deviceService) RemoveProvisionWatcher(name string) error {
 	pw, ok := cache.ProvisionWatchers().ForName(name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find ProvisionWatcher %s in cache", name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Removing managed ProvisionWatcher: %s", pw.Name)
+	s.lc.Debugf("Removing managed ProvisionWatcher: %s", pw.Name)
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.ProvisionWatcherClient.DeleteProvisionWatcherByName(ctx, name)
+	_, err := container.ProvisionWatcherClientFrom(s.dic.Get).DeleteProvisionWatcherByName(ctx, name)
 	if err != nil {
-		s.LoggingClient.Errorf("failed to delete ProvisionWatcher %s in Core Metadata", name)
+		s.lc.Errorf("failed to delete ProvisionWatcher %s in Core Metadata", name)
 		return err
 	}
 
@@ -81,21 +82,21 @@ func (s *DeviceService) RemoveProvisionWatcher(name string) error {
 
 // UpdateProvisionWatcher updates the Watcher in the cache and ensures that the
 // copy in Core Metadata is also updated.
-func (s *DeviceService) UpdateProvisionWatcher(watcher models.ProvisionWatcher) error {
+func (s *deviceService) UpdateProvisionWatcher(watcher models.ProvisionWatcher) error {
 	_, ok := cache.ProvisionWatchers().ForName(watcher.Name)
 	if !ok {
 		msg := fmt.Sprintf("failed to find ProvisionWatcher %s in cache", watcher.Name)
-		s.LoggingClient.Error(msg)
+		s.lc.Error(msg)
 		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, msg, nil)
 	}
 
-	s.LoggingClient.Debugf("Updating managed ProvisionWatcher: %s", watcher.Name)
+	s.lc.Debugf("Updating managed ProvisionWatcher: %s", watcher.Name)
 	req := requests.NewUpdateProvisionWatcherRequest(dtos.FromProvisionWatcherModelToUpdateDTO(watcher))
 	req.ProvisionWatcher.Id = nil
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
-	_, err := s.edgexClients.ProvisionWatcherClient.Update(ctx, []requests.UpdateProvisionWatcherRequest{req})
+	_, err := container.ProvisionWatcherClientFrom(s.dic.Get).Update(ctx, []requests.UpdateProvisionWatcherRequest{req})
 	if err != nil {
-		s.LoggingClient.Errorf("failed to update ProvisionWatcher %s in Core Metadata: %v", watcher.Name, err)
+		s.lc.Errorf("failed to update ProvisionWatcher %s in Core Metadata: %v", watcher.Name, err)
 		return err
 	}
 
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 0065f6ef..f2e162ec 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -1,194 +1,235 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
 // Copyright (C) 2017-2018 Canonical Ltd
-// Copyright (C) 2018-2022 IOTech Ltd
+// Copyright (C) 2018-2023 IOTech Ltd
 // Copyright (c) 2019 Intel Corporation
 //
 // SPDX-License-Identifier: Apache-2.0
 
-// This package provides a basic EdgeX Foundry device service implementation
+// Package service provides a basic EdgeX Foundry device service implementation
 // meant to be embedded in an application, similar in approach to the builtin
 // net/http package.
 package service
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net/http"
 	"os"
 	"strconv"
 	"sync"
 
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/clients"
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap"
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers"
+	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup"
+
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/autodiscovery"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/autoevent"
 	sdkCommon "github.com/edgexfoundry/device-sdk-go/v3/internal/common"
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/config"
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
 	restController "github.com/edgexfoundry/device-sdk-go/v3/internal/controller/http"
+	"github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces"
 	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
 
 	bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/config"
 	bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/flags"
-	"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces"
+	bootstrapInterfaces "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces"
 	bootstrapTypes "github.com/edgexfoundry/go-mod-bootstrap/v3/config"
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests"
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/errors"
+	edgexErr "github.com/edgexfoundry/go-mod-core-contracts/v3/errors"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
-	"github.com/edgexfoundry/go-mod-registry/v3/registry"
 	"github.com/google/uuid"
 	"github.com/gorilla/mux"
 )
 
-var (
-	ds *DeviceService
-)
-
-// UpdatableConfig interface allows services to have custom configuration populated from configuration stored
-// in the Configuration Provider (aka Consul). Services using custom configuration must implement this interface
-// on their custom configuration, even if they do not use Configuration Provider. If they do not use the
-// Configuration Provider they can have a dummy implementation of this interface.
-// This wraps the actual interface from go-mod-bootstrap so device service code doesn't have to have the additional
-// direct import of go-mod-bootstrap.
-type UpdatableConfig interface {
-	interfaces.UpdatableConfig
-}
-
-type DeviceService struct {
-	ServiceName     string
-	LoggingClient   logger.LoggingClient
-	RegistryClient  registry.Client
-	SecretProvider  interfaces.SecretProvider
-	MetricsManager  interfaces.MetricsManager
-	edgexClients    clients.EdgeXClients
-	controller      *restController.RestController
-	config          *config.ConfigurationStruct
-	deviceService   *models.DeviceService
-	driver          sdkModels.ProtocolDriver
-	discovery       sdkModels.ProtocolDiscovery
-	validator       sdkModels.DeviceValidator
-	manager         sdkModels.AutoEventManager
-	asyncCh         chan *sdkModels.AsyncValues
-	deviceCh        chan []sdkModels.DiscoveredDevice
-	initialized     bool
-	dic             *di.Container
-	flags           flags.Common
-	configProcessor *bootstrapConfig.Processor
-	ctx             context.Context
-	wg              *sync.WaitGroup
+const EnvInstanceName = "EDGEX_INSTANCE_NAME"
+
+type deviceService struct {
+	serviceKey         string
+	lc                 logger.LoggingClient
+	driver             interfaces.ProtocolDriver
+	discovery          interfaces.ProtocolDiscovery
+	validator          interfaces.DeviceValidator
+	autoEventManager   interfaces.AutoEventManager
+	controller         *restController.RestController
+	asyncCh            chan *sdkModels.AsyncValues
+	deviceCh           chan []sdkModels.DiscoveredDevice
+	flags              *flags.Default
+	deviceServiceModel *models.DeviceService
+	config             *config.ConfigurationStruct
+	configProcessor    *bootstrapConfig.Processor
+	wg                 *sync.WaitGroup
+	ctx                context.Context
+	dic                *di.Container
 }
 
-func (s *DeviceService) Initialize(serviceName, serviceVersion string, proto interface{}) {
-	if serviceName == "" {
-		_, _ = fmt.Fprintf(os.Stderr, "Please specify device service name")
-		os.Exit(1)
+func NewDeviceService(serviceKey string, serviceVersion string, driver any) (*deviceService, error) {
+	var service deviceService
+	if serviceKey == "" {
+		return nil, errors.New("please specify device service name")
 	}
-	s.ServiceName = serviceName
+	service.serviceKey = serviceKey
 
 	if serviceVersion == "" {
-		_, _ = fmt.Fprintf(os.Stderr, "Please specify device service version")
-		os.Exit(1)
+		return nil, errors.New("please specify device service version")
 	}
 	sdkCommon.ServiceVersion = serviceVersion
 
-	if driver, ok := proto.(sdkModels.ProtocolDriver); ok {
-		s.driver = driver
-	} else {
-		_, _ = fmt.Fprintf(os.Stderr, "Please implement and specify the protocoldriver")
-		os.Exit(1)
+	protocolDriver, ok := driver.(interfaces.ProtocolDriver)
+	if !ok {
+		return nil, errors.New("please implement and specify the ProtocolDriver")
 	}
+	service.driver = protocolDriver
 
-	if discovery, ok := proto.(sdkModels.ProtocolDiscovery); ok {
-		s.discovery = discovery
-	} else {
-		s.discovery = nil
+	if discovery, ok := driver.(interfaces.ProtocolDiscovery); ok {
+		service.discovery = discovery
 	}
 
-	if validator, ok := proto.(sdkModels.DeviceValidator); ok {
-		s.validator = validator
-	} else {
-		s.validator = nil
+	if validator, ok := driver.(interfaces.DeviceValidator); ok {
+		service.validator = validator
 	}
 
-	s.deviceService = &models.DeviceService{
-		Name: serviceName,
-	}
+	service.config = &config.ConfigurationStruct{}
+	return &service, nil
+}
+
+func (s *deviceService) Run() error {
+	var instanceName string
+	startupTimer := startup.NewStartUpTimer(s.serviceKey)
+
+	additionalUsage :=
+		"    -i, --instance                  Provides a service name suffix which allows unique instance to be created\n" +
+			"                                    If the option is provided, service name will be replaced with \"<name>_<instance>\"\n"
+	s.flags = flags.NewWithUsage(additionalUsage)
+	s.flags.FlagSet.StringVar(&instanceName, "instance", "", "")
+	s.flags.FlagSet.StringVar(&instanceName, "i", "", "")
+	s.flags.Parse(os.Args[1:])
+	s.setServiceName(instanceName)
 
 	s.config = &config.ConfigurationStruct{}
-}
+	s.deviceServiceModel = &models.DeviceService{Name: s.serviceKey}
+
+	s.dic = di.NewContainer(di.ServiceConstructorMap{
+		container.ConfigurationName: func(get di.Get) interface{} {
+			return s.config
+		},
+		container.DeviceServiceName: func(get di.Get) interface{} {
+			return s.deviceServiceModel
+		},
+		container.ProtocolDriverName: func(get di.Get) interface{} {
+			return s.driver
+		},
+		container.ProtocolDiscoveryName: func(get di.Get) interface{} {
+			return s.discovery
+		},
+		container.DeviceValidatorName: func(get di.Get) interface{} {
+			return s.validator
+		},
+	})
+
+	router := mux.NewRouter()
+	httpServer := handlers.NewHttpServer(router, true)
+
+	ctx, cancel := context.WithCancel(context.Background())
+	wg, deferred, successful := bootstrap.RunAndReturnWaitGroup(
+		ctx,
+		cancel,
+		s.flags,
+		s.serviceKey,
+		common.ConfigStemDevice,
+		s.config,
+		nil,
+		startupTimer,
+		s.dic,
+		true,
+		bootstrapTypes.ServiceTypeDevice,
+		[]bootstrapInterfaces.BootstrapHandler{
+			httpServer.BootstrapHandler,
+			messageBusBootstrapHandler,
+			handlers.NewServiceMetrics(s.serviceKey).BootstrapHandler, // Must be after Messaging
+			handlers.NewClientsBootstrap().BootstrapHandler,
+			autoevent.BootstrapHandler,
+			NewBootstrap(s, router).BootstrapHandler,
+			autodiscovery.BootstrapHandler,
+			handlers.NewStartMessage(s.serviceKey, sdkCommon.ServiceVersion).BootstrapHandler,
+		})
+
+	defer func() {
+		deferred()
+		s.Stop(false)
+	}()
+
+	if !successful {
+		cancel()
+		return errors.New("bootstrapping failed")
+	}
+
+	// TODO: call ProtocolDriver.Start() proposed in issue#1339
 
-func (s *DeviceService) UpdateFromContainer(r *mux.Router, dic *di.Container) {
-	s.LoggingClient = bootstrapContainer.LoggingClientFrom(dic.Get)
-	s.RegistryClient = bootstrapContainer.RegistryFrom(dic.Get)
-	s.SecretProvider = bootstrapContainer.SecretProviderFrom(dic.Get)
-	s.MetricsManager = bootstrapContainer.MetricsManagerFrom(dic.Get)
-	s.edgexClients.DeviceClient = bootstrapContainer.DeviceClientFrom(dic.Get)
-	s.edgexClients.DeviceServiceClient = bootstrapContainer.DeviceServiceClientFrom(dic.Get)
-	s.edgexClients.DeviceProfileClient = bootstrapContainer.DeviceProfileClientFrom(dic.Get)
-	s.edgexClients.ProvisionWatcherClient = bootstrapContainer.ProvisionWatcherClientFrom(dic.Get)
-	s.edgexClients.EventClient = bootstrapContainer.EventClientFrom(dic.Get)
-	s.config = container.ConfigurationFrom(dic.Get)
-	s.manager = container.ManagerFrom(dic.Get)
-	s.controller = restController.NewRestController(r, dic, s.ServiceName)
+	wg.Wait()
+	return nil
 }
 
 // Name returns the name of this Device Service
-func (s *DeviceService) Name() string {
-	return s.ServiceName
+func (s *deviceService) Name() string {
+	return s.serviceKey
 }
 
 // Version returns the version number of this Device Service
-func (s *DeviceService) Version() string {
+func (s *deviceService) Version() string {
 	return sdkCommon.ServiceVersion
 }
 
-// GetSecretProvider returns the SecretProvider
-func (s *DeviceService) GetSecretProvider() interfaces.SecretProvider {
-	return s.SecretProvider
+// SecretProvider returns the SecretProvider
+func (s *deviceService) SecretProvider() bootstrapInterfaces.SecretProvider {
+	return bootstrapContainer.SecretProviderFrom(s.dic.Get)
 }
 
-// GetMetricsManager returns the Metrics Manager used to register counter, gauge, gaugeFloat64 or timer metric types from
+// MetricsManager returns the Metrics Manager used to register counter, gauge, gaugeFloat64 or timer metric types from
 // github.com/rcrowley/go-metrics
-func (s *DeviceService) GetMetricsManager() interfaces.MetricsManager {
-	return s.MetricsManager
+func (s *deviceService) MetricsManager() bootstrapInterfaces.MetricsManager {
+	return bootstrapContainer.MetricsManagerFrom(s.dic.Get)
 }
 
-// GetLoggingClient returns the logger.LoggingClient
-func (s *DeviceService) GetLoggingClient() logger.LoggingClient {
-	return s.LoggingClient
+// LoggingClient returns the logger.LoggingClient
+func (s *deviceService) LoggingClient() logger.LoggingClient {
+	return s.lc
 }
 
 // AsyncReadings returns a bool value to indicate whether the asynchronous reading is enabled.
-func (s *DeviceService) AsyncReadings() bool {
+func (s *deviceService) AsyncReadingsEnabled() bool {
 	return s.config.Device.EnableAsyncReadings
 }
 
-func (s *DeviceService) DeviceDiscovery() bool {
+func (s *deviceService) AsyncValuesChannel() chan *sdkModels.AsyncValues {
+	return s.asyncCh
+}
+
+// DeviceDiscovery returns a bool value to indicate whether the device discovery is enabled.
+func (s *deviceService) DeviceDiscoveryEnabled() bool {
 	return s.config.Device.Discovery.Enabled
 }
 
-// AddRoute allows leveraging the existing internal web server to add routes specific to Device Service.
-func (s *DeviceService) AddRoute(route string, handler func(http.ResponseWriter, *http.Request), methods ...string) error {
-	return s.controller.AddRoute(route, handler, methods...)
+func (s *deviceService) DiscoveredDeviceChannel() chan []sdkModels.DiscoveredDevice {
+	return s.deviceCh
 }
 
-// Stop shuts down the Service
-func (s *DeviceService) Stop(force bool) {
-	if s.initialized {
-		err := s.driver.Stop(force)
-		if err != nil {
-			s.LoggingClient.Error(err.Error())
-		}
-	}
+// AddRoute allows leveraging the existing internal web server to add routes specific to Device Service.
+func (s *deviceService) AddRoute(route string, handler func(http.ResponseWriter, *http.Request), methods ...string) error {
+	return s.controller.AddRoute(route, handler, methods...)
 }
 
 // LoadCustomConfig uses the Config Processor from go-mod-bootstrap to attempt to load service's
 // custom configuration. It uses the same command line flags to process the custom config in the same manner
 // as the standard configuration.
-func (s *DeviceService) LoadCustomConfig(customConfig UpdatableConfig, sectionName string) error {
+func (s *deviceService) LoadCustomConfig(customConfig interfaces.UpdatableConfig, sectionName string) error {
 	if s.configProcessor == nil {
 		s.configProcessor = bootstrapConfig.NewProcessorForCustomConfig(s.flags, s.ctx, s.wg, s.dic)
 	}
@@ -205,7 +246,7 @@ func (s *DeviceService) LoadCustomConfig(customConfig UpdatableConfig, sectionNa
 // ListenForCustomConfigChanges uses the Config Processor from go-mod-bootstrap to attempt to listen for
 // changes to the specified custom configuration section. LoadCustomConfig must be called previously so that
 // the instance of svc.configProcessor has already been set.
-func (s *DeviceService) ListenForCustomConfigChanges(
+func (s *deviceService) ListenForCustomConfigChanges(
 	configToWatch interface{},
 	sectionName string,
 	changedCallback func(interface{})) error {
@@ -220,42 +261,43 @@ func (s *DeviceService) ListenForCustomConfigChanges(
 }
 
 // selfRegister register device service itself onto metadata.
-func (s *DeviceService) selfRegister() errors.EdgeX {
+func (s *deviceService) selfRegister() edgexErr.EdgeX {
 	localDeviceService := models.DeviceService{
-		Name:        s.ServiceName,
+		Name:        s.serviceKey,
 		Labels:      s.config.Device.Labels,
 		BaseAddress: bootstrapTypes.DefaultHttpProtocol + "://" + s.config.Service.Host + ":" + strconv.FormatInt(int64(s.config.Service.Port), 10),
 		AdminState:  models.Unlocked,
 	}
-	*s.deviceService = localDeviceService
+	*s.deviceServiceModel = localDeviceService
 	ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) // nolint:staticcheck
+	dsc := bootstrapContainer.DeviceServiceClientFrom(s.dic.Get)
 
-	s.LoggingClient.Debugf("trying to find device service %s", localDeviceService.Name)
-	res, err := s.edgexClients.DeviceServiceClient.DeviceServiceByName(ctx, localDeviceService.Name)
+	s.lc.Debugf("trying to find device service %s", localDeviceService.Name)
+	res, err := dsc.DeviceServiceByName(ctx, localDeviceService.Name)
 	if err != nil {
-		if errors.Kind(err) == errors.KindEntityDoesNotExist {
-			s.LoggingClient.Infof("device service %s doesn't exist, creating a new one", localDeviceService.Name)
+		if edgexErr.Kind(err) == edgexErr.KindEntityDoesNotExist {
+			s.lc.Infof("device service %s doesn't exist, creating a new one", localDeviceService.Name)
 			req := requests.NewAddDeviceServiceRequest(dtos.FromDeviceServiceModelToDTO(localDeviceService))
-			idRes, err := s.edgexClients.DeviceServiceClient.Add(ctx, []requests.AddDeviceServiceRequest{req})
+			idRes, err := dsc.Add(ctx, []requests.AddDeviceServiceRequest{req})
 			if err != nil {
-				s.LoggingClient.Errorf("failed to add device service %s: %v", localDeviceService.Name, err)
+				s.lc.Errorf("failed to add device service %s: %v", localDeviceService.Name, err)
 				return err
 			}
-			s.deviceService.Id = idRes[0].Id
-			s.LoggingClient.Debugf("new device service id: %s", localDeviceService.Id)
+			s.deviceServiceModel.Id = idRes[0].Id
+			s.lc.Debugf("new device service id: %s", localDeviceService.Id)
 		} else {
-			s.LoggingClient.Errorf("failed to find device service %s", localDeviceService.Name)
+			s.lc.Errorf("failed to find device service %s", localDeviceService.Name)
 			return err
 		}
 	} else {
-		s.LoggingClient.Infof("device service %s exists, updating it", s.ServiceName)
+		s.lc.Infof("device service %s exists, updating it", s.serviceKey)
 		req := requests.NewUpdateDeviceServiceRequest(dtos.FromDeviceServiceModelToUpdateDTO(localDeviceService))
 		req.Service.Id = nil
-		_, err = s.edgexClients.DeviceServiceClient.Update(ctx, []requests.UpdateDeviceServiceRequest{req})
+		_, err = dsc.Update(ctx, []requests.UpdateDeviceServiceRequest{req})
 		if err != nil {
-			s.LoggingClient.Errorf("failed to update device service %s with local config: %v", localDeviceService.Name, err)
+			s.lc.Errorf("failed to update device service %s with local config: %v", localDeviceService.Name, err)
 			oldDeviceService := dtos.ToDeviceServiceModel(res.Service)
-			*s.deviceService = oldDeviceService
+			*s.deviceServiceModel = oldDeviceService
 		}
 	}
 
@@ -263,28 +305,25 @@ func (s *DeviceService) selfRegister() errors.EdgeX {
 }
 
 // DriverConfigs retrieves the driver specific configuration
-func (s *DeviceService) DriverConfigs() map[string]string {
+func (s *deviceService) DriverConfigs() map[string]string {
 	return s.config.Driver
 }
 
-// SetDeviceOpState sets the operating state of device
-func (s *DeviceService) SetDeviceOpState(name string, state models.OperatingState) error {
-	d, err := s.GetDeviceByName(name)
+// Stop shuts down the Service
+func (s *deviceService) Stop(force bool) {
+	err := s.driver.Stop(force)
 	if err != nil {
-		return err
+		s.lc.Errorf(err.Error())
 	}
-
-	d.OperatingState = state
-	return s.UpdateDevice(d)
 }
 
-// RunningService returns the Service instance which is running
-func RunningService() *DeviceService {
-	return ds
-}
+func (s *deviceService) setServiceName(instanceName string) {
+	envValue := os.Getenv(EnvInstanceName)
+	if len(envValue) > 0 {
+		instanceName = envValue
+	}
 
-// DriverConfigs retrieves the driver specific configuration
-// TODO remove this in EDGEX3.0
-func DriverConfigs() map[string]string {
-	return ds.config.Driver
+	if len(instanceName) > 0 {
+		s.serviceKey = s.serviceKey + "_" + instanceName
+	}
 }
diff --git a/pkg/startup/bootstrap.go b/pkg/startup/bootstrap.go
index 397d0df0..6578d5ec 100644
--- a/pkg/startup/bootstrap.go
+++ b/pkg/startup/bootstrap.go
@@ -1,20 +1,31 @@
 // -*- Mode: Go; indent-tabs-mode: t -*-
 //
 // Copyright (C) 2017-2018 Canonical Ltd
-// Copyright (C) 2018-2020 IOTech Ltd
+// Copyright (C) 2018-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
 package startup
 
 import (
-	"context"
+	"fmt"
+	"os"
 
 	"github.com/edgexfoundry/device-sdk-go/v3/pkg/service"
-	"github.com/gorilla/mux"
 )
 
-func Bootstrap(serviceName string, serviceVersion string, driver interface{}) {
-	ctx, cancel := context.WithCancel(context.Background())
-	service.Main(serviceName, serviceVersion, driver, ctx, cancel, mux.NewRouter())
+func Bootstrap(serviceKey string, serviceVersion string, driver any) {
+	service, err := service.NewDeviceService(serviceKey, serviceVersion, driver)
+	if err != nil {
+		_, _ = fmt.Fprint(os.Stderr, err.Error())
+		os.Exit(-1)
+	}
+
+	err = service.Run()
+	if err != nil {
+		service.LoggingClient().Errorf("Device Service %s", err.Error())
+		os.Exit(-1)
+	}
+
+	os.Exit(0)
 }


Test patch:
diff --git a/internal/controller/http/command_test.go b/internal/controller/http/command_test.go
index fcc5b00b..544af2da 100644
--- a/internal/controller/http/command_test.go
+++ b/internal/controller/http/command_test.go
@@ -31,8 +31,8 @@ import (
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/cache"
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/config"
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
+	"github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces/mocks"
 	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
-	"github.com/edgexfoundry/device-sdk-go/v3/pkg/models/mocks"
 )
 
 const (
diff --git a/internal/controller/messaging/validation_test.go b/internal/controller/messaging/validation_test.go
index bf8ba75a..ce76afcb 100644
--- a/internal/controller/messaging/validation_test.go
+++ b/internal/controller/messaging/validation_test.go
@@ -29,7 +29,7 @@ import (
 
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/config"
 	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
-	"github.com/edgexfoundry/device-sdk-go/v3/pkg/models/mocks"
+	"github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces/mocks"
 )
 
 const (
diff --git a/internal/transformer/transform_test.go b/internal/transformer/transform_test.go
index 21b3b9b6..88df283a 100644
--- a/internal/transformer/transform_test.go
+++ b/internal/transformer/transform_test.go
@@ -11,23 +11,23 @@ import (
 	"math/rand"
 	"testing"
 
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/cache"
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/config"
-	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
-	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
-	"github.com/edgexfoundry/device-sdk-go/v3/pkg/models/mocks"
 	bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
 	"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
 	clientMocks "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/interfaces/mocks"
+	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/common"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos"
 	dtoCommon "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/responses"
 	"github.com/edgexfoundry/go-mod-core-contracts/v3/models"
-
-	"github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/cache"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/config"
+	"github.com/edgexfoundry/device-sdk-go/v3/internal/container"
+	"github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces/mocks"
+	sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
 )
 
 const (
diff --git a/pkg/service/async_test.go b/pkg/service/async_test.go
index 8744ac67..b79d72ba 100644
--- a/pkg/service/async_test.go
+++ b/pkg/service/async_test.go
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2020-2021 IOTech Ltd
+// Copyright (C) 2020-2023 IOTech Ltd
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -19,8 +19,15 @@ var d = sdkModels.DiscoveredDevice{
 	Name: "device-sdk-test",
 }
 
+func newDeviceService() *deviceService {
+	return &deviceService{
+		serviceKey: "test-service",
+		lc:         logger.NewMockClient(),
+	}
+}
+
 func Test_checkAllowList(t *testing.T) {
-	lc := logger.NewMockClient()
+	ds := newDeviceService()
 	pw := models.ProvisionWatcher{
 		Name: "test-watcher",
 		Identifiers: map[string]string{
@@ -85,14 +92,14 @@ func Test_checkAllowList(t *testing.T) {
 	for _, testCase := range tests {
 		t.Run(testCase.name, func(t *testing.T) {
 			d.Protocols = testCase.protocols
-			result := checkAllowList(d, pw, lc)
+			result := ds.checkAllowList(d, pw)
 			assert.Equal(t, testCase.expected, result)
 		})
 	}
 }
 
 func Test_checkBlockList(t *testing.T) {
-	lc := logger.NewMockClient()
+	ds := newDeviceService()
 	pw := models.ProvisionWatcher{
 		Name: "test-watcher",
 		BlockingIdentifiers: map[string][]string{
@@ -141,7 +148,7 @@ func Test_checkBlockList(t *testing.T) {
 	for _, testCase := range tests {
 		t.Run(testCase.name, func(t *testing.T) {
 			d.Protocols = testCase.protocols
-			result := checkBlockList(d, pw, lc)
+			result := ds.checkBlockList(d, pw)
 			assert.Equal(t, testCase.expected, result)
 		})
 	}