diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 7369a09ecc21085b97baa88f431727329f08fc46..99340e2ed14ea535c7389ada1a01b8a0a757e084 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -24,6 +24,7 @@
 #include "core/hle/service/nvdrv/nvdrv.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/service.h"
 #include "core/hle/service/vi/vi.h"
 #include "core/hle/service/vi/vi_m.h"
 #include "core/hle/service/vi/vi_s.h"
@@ -1202,26 +1203,18 @@ IApplicationDisplayService::IApplicationDisplayService(
     RegisterHandlers(functions);
 }
 
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
-                             std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
-    : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
-
-Module::Interface::~Interface() = default;
-
-void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
-    LOG_WARNING(Service_VI, "(STUBBED) called");
-
+void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
+                                   std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
+    rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
 }
 
 void InstallInterfaces(SM::ServiceManager& service_manager,
                        std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
-    auto module = std::make_shared<Module>();
-    std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
-    std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
-    std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
+    std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
+    std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
+    std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
 }
 
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index e3963502a767f5fe460c4a28da346fb8e8092999..c5682accc947be1403abda37b60013417d64a1c9 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -4,13 +4,26 @@
 
 #pragma once
 
-#include "core/hle/service/service.h"
+#include <memory>
+#include "common/common_types.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
 
 namespace Service::NVFlinger {
 class NVFlinger;
 }
 
+namespace Service::SM {
+class ServiceManager;
+}
+
 namespace Service::VI {
+namespace detail {
+void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
+                           std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+}
 
 enum class DisplayResolution : u32 {
     DockedWidth = 1920,
@@ -19,22 +32,6 @@ enum class DisplayResolution : u32 {
     UndockedHeight = 720,
 };
 
-class Module final {
-public:
-    class Interface : public ServiceFramework<Interface> {
-    public:
-        explicit Interface(std::shared_ptr<Module> module, const char* name,
-                           std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
-        ~Interface() override;
-
-        void GetDisplayService(Kernel::HLERequestContext& ctx);
-
-    protected:
-        std::shared_ptr<Module> module;
-        std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
-    };
-};
-
 /// Registers all VI services with the specified service manager.
 void InstallInterfaces(SM::ServiceManager& service_manager,
                        std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 207c06b16ed9c9f319045c911ca7e65a2a691ce9..6e3e0bd8f5866b41aba77775b4b7b5bd53560328 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -2,12 +2,14 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "common/logging/log.h"
+#include "core/hle/service/vi/vi.h"
 #include "core/hle/service/vi/vi_m.h"
 
 namespace Service::VI {
 
-VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
-    : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
+VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
+    : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
     static const FunctionInfo functions[] = {
         {2, &VI_M::GetDisplayService, "GetDisplayService"},
         {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
 
 VI_M::~VI_M() = default;
 
+void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_VI, "(STUBBED) called");
+
+    detail::GetDisplayServiceImpl(ctx, nv_flinger);
+}
+
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 487d58d5098ffd958faad6c87dfcdc760dc7acad..290e066891b2aa1e4b5ba6aa7f6de548965916f3 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -4,14 +4,27 @@
 
 #pragma once
 
-#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
+
+namespace Service::NVFlinger {
+class NVFlinger;
+}
 
 namespace Service::VI {
 
-class VI_M final : public Module::Interface {
+class VI_M final : public ServiceFramework<VI_M> {
 public:
-    explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+    explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
     ~VI_M() override;
+
+private:
+    void GetDisplayService(Kernel::HLERequestContext& ctx);
+
+    std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
 };
 
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 920e6a1f69c16832d6f6f134fd31980be6aab330..6dd700eaec6337e7f983368d2ba653cbc4080615 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -2,12 +2,14 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "common/logging/log.h"
+#include "core/hle/service/vi/vi.h"
 #include "core/hle/service/vi/vi_s.h"
 
 namespace Service::VI {
 
-VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
-    : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
+VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
+    : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
     static const FunctionInfo functions[] = {
         {1, &VI_S::GetDisplayService, "GetDisplayService"},
         {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
 
 VI_S::~VI_S() = default;
 
+void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_VI, "(STUBBED) called");
+
+    detail::GetDisplayServiceImpl(ctx, nv_flinger);
+}
+
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index bbc31148f4104c8bbb05bbc3d28ffb9b732358cf..47804dc0be66af8810195fa3db3437a9986331c3 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -4,14 +4,27 @@
 
 #pragma once
 
-#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
+
+namespace Service::NVFlinger {
+class NVFlinger;
+}
 
 namespace Service::VI {
 
-class VI_S final : public Module::Interface {
+class VI_S final : public ServiceFramework<VI_S> {
 public:
-    explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+    explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
     ~VI_S() override;
+
+private:
+    void GetDisplayService(Kernel::HLERequestContext& ctx);
+
+    std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
 };
 
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index d81e410d6de2167b2ac865c729de5ddee1ae3e73..ef09a5df5de2ef3d0fc65d31bb6de1fec4045ef1 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -2,12 +2,14 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "common/logging/log.h"
+#include "core/hle/service/vi/vi.h"
 #include "core/hle/service/vi/vi_u.h"
 
 namespace Service::VI {
 
-VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
-    : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
+VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
+    : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
     static const FunctionInfo functions[] = {
         {0, &VI_U::GetDisplayService, "GetDisplayService"},
     };
@@ -16,4 +18,10 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
 
 VI_U::~VI_U() = default;
 
+void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_VI, "(STUBBED) called");
+
+    detail::GetDisplayServiceImpl(ctx, nv_flinger);
+}
+
 } // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index b92f28c92aa76820c60763f75bb4f5dca382b00f..19bdb73b0b4a5d6a56d6eb89509fd91023043073 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -4,14 +4,27 @@
 
 #pragma once
 
-#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
+
+namespace Service::NVFlinger {
+class NVFlinger;
+}
 
 namespace Service::VI {
 
-class VI_U final : public Module::Interface {
+class VI_U final : public ServiceFramework<VI_U> {
 public:
-    explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+    explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
     ~VI_U() override;
+
+private:
+    void GetDisplayService(Kernel::HLERequestContext& ctx);
+
+    std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
 };
 
 } // namespace Service::VI