This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new cb92c4f  ui: restructure src/ by role — shell / controls / state / 
features / layer / render
cb92c4f is described below

commit cb92c4f747e3d549842409eb68e07339ad94c3b9
Author: Wu Sheng <[email protected]>
AuthorDate: Sat May 16 23:41:05 2026 +0800

    ui: restructure src/ by role — shell / controls / state / features / layer 
/ render
    
    Apply the CLAUDE.md UI-layering principle to the codebase. Feature code
    moves WITH its feature (high cohesion); shared keeps only feature-
    agnostic primitives + helpers (low coupling).
    
    Top-level layout:
    
      api/                  façade + scopes (unchanged)
      shell/                AppShell / AppSidebar / AppTopbar / banners /
                            placeholders + router/index.ts + framework
                            composables (useLayers, useLandingOrder,
                            useOapInfo, useAdminFeatures)
      controls/             time-range store, auto-refresh ticker +
                            subscriber, client-id (cross-cutting controls)
      state/                auth, setup (global app state)
      features/
        auth/               LoginView
        alarms/             AlarmsView + AlarmDetailPanel
        setup/              SetupView + LayerSetupCard
        admin/{layer-templates,alert-page}/
        operate/{cluster,inspect,dsl,live-debug}/
                + operate/_shared/ (Modal, MonacoYaml, MonacoDiff,
                                    RuleCard, DestructiveConfirm,
                                    grouping — shared by dsl + live-debug)
                DSL composable (useRuleEditor) co-located with dsl/;
                debug composables co-located with live-debug/
      layer/                LayerShell + LayerServiceSelector +
                            LayerTabPlaceholder + shared layer composables
                            (useLayerLanding/Endpoints/Instances +
                            useSelectedService/Instance/Endpoint), with
                            each tab in its own subfolder:
        traces/             LayerTracesView + LayerZipkinTracesView +
                            NativeTraceWaterfall + TracePopout +
                            ZipkinTracePopout + useLayerTraces +
                            useZipkinTraces + useTracePopout +
                            useZipkinTracePopout
        logs/               LayerLogsView + useLayerLogs
        service-map/        LayerServiceMapView + useLayerTopology +
                            useTopologyIcons
        endpoint-dependency/ LayerEndpointDependencyView + composable
        profiling/          all 5 Layer*ProfilingView + ProfileFlameGraph +
                            ProfileStackTable + ProfileStackRow +
                            ProcessTopologyGraph (flame-graph renderers
                            used only by profiling)
      render/               template-driven render layer
        overview/           OverviewLanding + OverviewDashboardView +
                            useOverviewDashboard(s)
        layer-dashboard/    LayerDashboardsView + useLayerDashboard
        widgets/            AlarmsWidget / K8sSummaryWidget /
                            KpiTileWidget / MetricWidget /
                            PilotSummaryWidget / SectionBreak /
                            ServiceCountWidget / ValueFormat
      components/           feature-agnostic shared primitives
        primitives/ charts/ icons/
      monaco/ utils/ assets/  unchanged
    
    Net: 78 file moves + 182 imports rewritten across 58 files by script.
    Empty source dirs (views/, stores/, composables/, components/{shell,
    operate,overview,profile,trace}, router/) removed. CLAUDE.md UI-
    layering principle updated to describe the landed shape. tsc / vue-tsc
    / license-eye all green. Pure restructure — no behavior changes.
---
 CLAUDE.md                                          | 18 +++---
 apps/ui/src/{stores => controls}/autoRefresh.ts    |  0
 .../useClientId.ts => controls/clientId.ts}        |  0
 apps/ui/src/{stores => controls}/timeRange.ts      |  0
 .../useAutoRefreshSubscribe.ts                     |  2 +-
 .../admin/alert-page}/AlertPageSetupView.vue       |  0
 .../layer-templates}/LayerDashboardsAdmin.vue      |  0
 .../admin/layer-templates}/widget-mock.ts          |  0
 .../alarms/AlarmDetailPanel.vue                    |  0
 .../src/{views => features}/alarms/AlarmsView.vue  |  2 +-
 apps/ui/src/{views => features}/auth/LoginView.vue |  2 +-
 .../operate/_shared}/DestructiveConfirm.vue        |  0
 .../operate => features/operate/_shared}/Modal.vue |  0
 .../operate/_shared}/MonacoDiff.vue                |  2 +-
 .../operate/_shared}/MonacoYaml.vue                |  2 +-
 .../operate/_shared}/RuleCard.vue                  |  0
 .../operate/_shared}/grouping.ts                   |  0
 .../operate/cluster}/ClusterStatusView.vue         |  4 +-
 .../operate/dsl/DslCatalogView.vue                 |  6 +-
 .../operate/dsl/DslDumpView.vue                    |  4 +-
 .../operate/dsl/DslEditorView.vue                  | 12 ++--
 .../operate/dsl/OalCatalogView.vue                 |  2 +-
 .../operate/dsl/syntaxHighlight.ts                 |  0
 .../operate/dsl}/useRuleEditor.ts                  |  0
 .../operate/inspect}/InspectView.vue               |  2 +-
 .../operate/live-debug/DebugHistoryView.vue        |  2 +-
 .../operate/live-debug/DebugLal.vue                |  4 +-
 .../operate/live-debug/DebugMal.vue                |  4 +-
 .../operate/live-debug/DebugOal.vue                |  4 +-
 .../operate/live-debug/DebugView.vue               |  0
 .../operate/live-debug/LiveDebuggerView.vue        |  2 +-
 .../operate/live-debug/NodeCoverage.vue            |  0
 .../operate/live-debug/constants.ts                |  0
 .../operate/live-debug/oalEntityId.ts              |  0
 .../operate/live-debug/payload.ts                  |  0
 .../operate/live-debug}/useDebugHistory.ts         |  0
 .../operate/live-debug}/useDebugSession.ts         |  2 +-
 .../{views => features}/setup/LayerSetupCard.vue   |  2 +-
 .../ui/src/{views => features}/setup/SetupView.vue |  2 +-
 .../src/{views => }/layer/LayerServiceSelector.vue |  4 +-
 apps/ui/src/{views => }/layer/LayerShell.vue       | 12 ++--
 .../src/{views => }/layer/LayerTabPlaceholder.vue  |  2 +-
 .../LayerEndpointDependencyView.vue                | 14 ++---
 .../useLayerEndpointDependency.ts                  |  2 +-
 .../{views/layer => layer/logs}/LayerLogsView.vue  | 20 +++----
 .../{composables => layer/logs}/useLayerLogs.ts    |  2 +-
 .../profiling}/LayerAsyncProfilingView.vue         |  6 +-
 .../profiling}/LayerEBPFProfilingView.vue          | 12 ++--
 .../profiling}/LayerNetworkProfilingView.vue       |  6 +-
 .../profiling}/LayerPprofProfilingView.vue         |  6 +-
 .../profiling}/LayerTraceProfilingView.vue         | 16 +++---
 .../profiling}/ProcessTopologyGraph.vue            |  0
 .../profiling}/ProfileFlameGraph.vue               |  0
 .../profiling}/ProfileStackRow.vue                 |  0
 .../profiling}/ProfileStackTable.vue               |  0
 .../service-map}/LayerServiceMapView.vue           | 10 ++--
 .../service-map}/useLayerTopology.ts               |  2 +-
 .../service-map}/useTopologyIcons.ts               |  0
 .../layer => layer/traces}/LayerTracesEntry.vue    |  2 +-
 .../layer => layer/traces}/LayerTracesView.vue     | 18 +++---
 .../traces}/LayerZipkinTracesView.vue              |  4 +-
 .../traces}/NativeTraceWaterfall.vue               |  2 +-
 .../trace => layer/traces}/TracePopout.vue         |  6 +-
 .../trace => layer/traces}/ZipkinTracePopout.vue   |  4 +-
 .../traces}/useLayerTraces.ts                      |  0
 .../traces}/useTracePopout.ts                      |  0
 .../traces}/useZipkinTracePopout.ts                |  0
 .../traces}/useZipkinTraces.ts                     |  0
 .../{composables => layer}/useLayerEndpoints.ts    |  0
 .../{composables => layer}/useLayerInstances.ts    |  0
 .../src/{composables => layer}/useLayerLanding.ts  |  2 +-
 .../{composables => layer}/useSelectedEndpoint.ts  |  0
 .../{composables => layer}/useSelectedInstance.ts  |  0
 .../{composables => layer}/useSelectedService.ts   |  0
 apps/ui/src/main.ts                                |  4 +-
 .../layer-dashboard}/LayerDashboardsView.vue       | 20 +++----
 .../layer-dashboard}/useLayerDashboard.ts          |  2 +-
 .../overview/OverviewDashboardView.vue             | 18 +++---
 .../{views => render}/overview/OverviewLanding.vue |  4 +-
 .../overview}/useOverviewDashboard.ts              |  0
 .../overview}/useOverviewDashboards.ts             |  2 +-
 .../overview => render}/widgets/AlarmsWidget.vue   |  0
 .../widgets/K8sSummaryWidget.vue                   |  2 +-
 .../overview => render}/widgets/KpiTileWidget.vue  |  0
 .../overview => render}/widgets/MetricWidget.vue   |  0
 .../widgets/PilotSummaryWidget.vue                 |  2 +-
 .../overview => render}/widgets/SectionBreak.vue   |  0
 .../widgets/ServiceCountWidget.vue                 |  0
 .../overview => render}/widgets/ValueFormat.ts     |  0
 .../{components => }/shell/AdminFeatureWarning.vue |  2 +-
 apps/ui/src/{components => }/shell/AppShell.vue    |  4 +-
 apps/ui/src/{components => }/shell/AppSidebar.vue  |  8 +--
 apps/ui/src/{components => }/shell/AppTopbar.vue   |  8 +--
 .../shell/GlobalConnectivityBanner.vue             |  2 +-
 .../src/{views/landing => shell}/LandingView.vue   |  0
 apps/ui/src/{views => shell}/PlaceholderView.vue   |  0
 apps/ui/src/{ => shell}/router/index.ts            | 64 +++++++++++-----------
 .../src/{composables => shell}/useAdminFeatures.ts |  2 +-
 .../src/{composables => shell}/useLandingOrder.ts  |  2 +-
 apps/ui/src/{composables => shell}/useLayers.ts    |  0
 apps/ui/src/{composables => shell}/useOapInfo.ts   |  2 +-
 apps/ui/src/{stores => state}/auth.ts              |  0
 apps/ui/src/{stores => state}/setup.ts             |  2 +-
 .../ui/src/{composables => utils}/metricCatalog.ts |  0
 apps/ui/src/{composables => utils}/metricColor.ts  |  0
 105 files changed, 192 insertions(+), 190 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 183fdbf..f2fa042 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -74,14 +74,16 @@ Design tokens have been lifted into the runtime token CSS — 
that copy is canon
   - `util/` — pure helpers used anywhere (time formatting, MQE target/catalog 
cache, trace-protocol cache).
   - `user/` + `rbac/` — session/auth + verb policy, enforced at the http edge.
   Don't import `client/` from `http/` directly; route through `logic/` so the 
orchestration layer stays the seam.
-- **Layering — UI.** `apps/ui/src/` follows the same role-based grouping. When 
adding code, decide which layer it belongs in *before* picking a file name. A 
file mixing two layers means it should be split.
-  - **Shell / framework** — `components/shell/`, `router/`, `App.vue`. Chrome 
that every page lives inside (sidebar, topbar, breadcrumbs, layer-tab strip). 
Knows about layers and routes, never about specific feature data.
-  - **Cross-cutting state** — `stores/timeRange.ts`, the auto-refresh ticker, 
`stores/auth.ts`, `composables/useClientId.ts`. Lives above pages; pages 
subscribe, never own.
-  - **API client** — `apps/ui/src/api/`. Façade `bff.<scope>.<method>()` is 
the only path to the BFF; no `fetch()` calls anywhere else.
-  - **Composables** — `composables/`. Reactive data fetchers + business state 
(one composable per query family); pages compose them rather than inlining 
`useQuery` inside a view.
-  - **Static feature pages** — `views/operate/{inspect,dsl,live-debug}/`, 
`views/alarms/`, `views/auth/`, `views/setup/`. Bespoke layouts (not 
template-driven). Own their page-local components but reuse primitives + charts.
-  - **Configurable render** — `views/overview/`, `views/layer/Layer*View.vue` 
widget grids. Layout + widget set come from JSON templates served by the BFF; 
the page is a generic renderer driven by config. New dashboards mean new 
templates, not new Vue files.
-  - **Primitives + shared visuals** — `components/primitives/`, 
`components/charts/`, `components/icons/`. Stateless, no business logic. If a 
primitive needs feature data, it's in the wrong layer.
+- **Layering — UI.** `apps/ui/src/` follows the same role-based grouping. The 
guiding rule is **feature code stays cohesive with its feature; shared code is 
feature-AGNOSTIC only** (fonts, styles, formatters, generic primitives, charts 
wrappers). A component or composable that knows about a feature's data lives 
WITH the feature, not in a shared pile. Features don't import from each other.
+  - **`api/`** — façade `bff.<scope>.<method>()` over the BFF. Only path to 
HTTP; no `fetch()` calls anywhere else.
+  - **`shell/`** — chrome every page lives in (AppShell / AppSidebar / 
AppTopbar / GlobalConnectivityBanner / AdminFeatureWarning / PlaceholderView / 
LandingView), plus `router/index.ts` and the framework-level composables the 
sidebar/topbar need (`useLayers`, `useLandingOrder`, `useOapInfo`, 
`useAdminFeatures`). Knows about layers + routes, never about specific feature 
data.
+  - **`controls/`** — cross-cutting controls owned by the topbar / shell. The 
time-range store, the auto-refresh ticker + its subscriber, the per-session 
client id. Pages subscribe, never own.
+  - **`state/`** — global app state (`auth`, `setup`). Pinia stores that 
survive route changes.
+  - **`features/<feature>/`** — static feature pages. Each folder is fully 
self-contained: its views, its composables, its feature-specific components. 
Operate sub-features (`cluster/`, `inspect/`, `dsl/`, `live-debug/`) share 
their cross-cutting bits via `features/operate/_shared/` (Modal / MonacoYaml / 
MonacoDiff / RuleCard / DestructiveConfirm / grouping).
+  - **`layer/<tab>/`** — the per-layer drill-down stack. Top-level files are 
the shell (`LayerShell`, `LayerServiceSelector`, `LayerTabPlaceholder`) plus 
shared layer composables (`useLayerLanding`, `useLayerEndpoints`, 
`useLayerInstances`, `useSelectedService/Instance/Endpoint`). Each tab 
subfolder owns its view + composables + tab-specific components (e.g. 
`layer/traces/` contains `LayerTracesView.vue` + `NativeTraceWaterfall.vue` + 
`TracePopout.vue` + `useLayerTraces.ts`).
+  - **`render/`** — template-driven render. `render/overview/` and 
`render/layer-dashboard/` are generic renderers driven by JSON templates from 
the BFF; `render/widgets/` holds the reusable widget primitives (AlarmsWidget, 
MetricWidget, ServiceCountWidget, …). New dashboards mean new templates, not 
new Vue files.
+  - **`components/{primitives,charts,icons}/`** — feature-agnostic shared 
building blocks. Stateless, no business logic. If a component starts needing 
feature data, move it INTO the feature; don't enrich the shared pile.
+  - **`monaco/`, `utils/`, `assets/`** — same shared-is-feature-agnostic rule: 
editor setup, formatters, stylesheets, icon SVGs. New helpers land here only if 
more than one feature needs them and they don't carry feature semantics.
 
 ## Commits & PRs
 
diff --git a/apps/ui/src/stores/autoRefresh.ts 
b/apps/ui/src/controls/autoRefresh.ts
similarity index 100%
rename from apps/ui/src/stores/autoRefresh.ts
rename to apps/ui/src/controls/autoRefresh.ts
diff --git a/apps/ui/src/composables/useClientId.ts 
b/apps/ui/src/controls/clientId.ts
similarity index 100%
rename from apps/ui/src/composables/useClientId.ts
rename to apps/ui/src/controls/clientId.ts
diff --git a/apps/ui/src/stores/timeRange.ts b/apps/ui/src/controls/timeRange.ts
similarity index 100%
rename from apps/ui/src/stores/timeRange.ts
rename to apps/ui/src/controls/timeRange.ts
diff --git a/apps/ui/src/composables/useAutoRefreshSubscribe.ts 
b/apps/ui/src/controls/useAutoRefreshSubscribe.ts
similarity index 95%
rename from apps/ui/src/composables/useAutoRefreshSubscribe.ts
rename to apps/ui/src/controls/useAutoRefreshSubscribe.ts
index 81eb441..6c7aec9 100644
--- a/apps/ui/src/composables/useAutoRefreshSubscribe.ts
+++ b/apps/ui/src/controls/useAutoRefreshSubscribe.ts
@@ -16,7 +16,7 @@
  */
 
 import { watch } from 'vue';
-import { useAutoRefreshStore } from '@/stores/autoRefresh';
+import { useAutoRefreshStore } from '@/controls/autoRefresh';
 
 /**
  * Subscribe a refetch callback to the global auto-refresh ticker.
diff --git a/apps/ui/src/views/admin/AlertPageSetupView.vue 
b/apps/ui/src/features/admin/alert-page/AlertPageSetupView.vue
similarity index 100%
rename from apps/ui/src/views/admin/AlertPageSetupView.vue
rename to apps/ui/src/features/admin/alert-page/AlertPageSetupView.vue
diff --git a/apps/ui/src/views/admin/LayerDashboardsAdmin.vue 
b/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
similarity index 100%
rename from apps/ui/src/views/admin/LayerDashboardsAdmin.vue
rename to apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
diff --git a/apps/ui/src/views/admin/widget-mock.ts 
b/apps/ui/src/features/admin/layer-templates/widget-mock.ts
similarity index 100%
rename from apps/ui/src/views/admin/widget-mock.ts
rename to apps/ui/src/features/admin/layer-templates/widget-mock.ts
diff --git a/apps/ui/src/views/alarms/AlarmDetailPanel.vue 
b/apps/ui/src/features/alarms/AlarmDetailPanel.vue
similarity index 100%
rename from apps/ui/src/views/alarms/AlarmDetailPanel.vue
rename to apps/ui/src/features/alarms/AlarmDetailPanel.vue
diff --git a/apps/ui/src/views/alarms/AlarmsView.vue 
b/apps/ui/src/features/alarms/AlarmsView.vue
similarity index 99%
rename from apps/ui/src/views/alarms/AlarmsView.vue
rename to apps/ui/src/features/alarms/AlarmsView.vue
index 16e09e6..c8301b9 100644
--- a/apps/ui/src/views/alarms/AlarmsView.vue
+++ b/apps/ui/src/features/alarms/AlarmsView.vue
@@ -41,7 +41,7 @@ import {
   type AlarmTrafficPoint,
   type AlarmTrafficSeries,
 } from '@/api/client';
-import { useLayers } from '@/composables/useLayers';
+import { useLayers } from '@/shell/useLayers';
 import AlarmsTimeline from '@/components/charts/AlarmsTimeline.vue';
 import AlarmDetailPanel from './AlarmDetailPanel.vue';
 
diff --git a/apps/ui/src/views/auth/LoginView.vue 
b/apps/ui/src/features/auth/LoginView.vue
similarity index 99%
rename from apps/ui/src/views/auth/LoginView.vue
rename to apps/ui/src/features/auth/LoginView.vue
index dca2f21..c3d670a 100644
--- a/apps/ui/src/views/auth/LoginView.vue
+++ b/apps/ui/src/features/auth/LoginView.vue
@@ -18,7 +18,7 @@
 import { ref } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import logoSw from '@/assets/icons/logo-sw.svg?raw';
-import { useAuthStore } from '@/stores/auth';
+import { useAuthStore } from '@/state/auth';
 
 const auth = useAuthStore();
 const router = useRouter();
diff --git a/apps/ui/src/components/operate/DestructiveConfirm.vue 
b/apps/ui/src/features/operate/_shared/DestructiveConfirm.vue
similarity index 100%
rename from apps/ui/src/components/operate/DestructiveConfirm.vue
rename to apps/ui/src/features/operate/_shared/DestructiveConfirm.vue
diff --git a/apps/ui/src/components/operate/Modal.vue 
b/apps/ui/src/features/operate/_shared/Modal.vue
similarity index 100%
rename from apps/ui/src/components/operate/Modal.vue
rename to apps/ui/src/features/operate/_shared/Modal.vue
diff --git a/apps/ui/src/components/operate/MonacoDiff.vue 
b/apps/ui/src/features/operate/_shared/MonacoDiff.vue
similarity index 97%
rename from apps/ui/src/components/operate/MonacoDiff.vue
rename to apps/ui/src/features/operate/_shared/MonacoDiff.vue
index a86ac4a..a18f2dc 100644
--- a/apps/ui/src/components/operate/MonacoDiff.vue
+++ b/apps/ui/src/features/operate/_shared/MonacoDiff.vue
@@ -17,7 +17,7 @@
 <script setup lang="ts">
 import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
 import * as monaco from 'monaco-editor';
-import { setupMonaco, RR_THEME_NAME } from '../../monaco/setup.js';
+import { setupMonaco, RR_THEME_NAME } from '../../../monaco/setup.js';
 
 const props = defineProps<{
   /** "before" — what's currently on the server (or bundled). */
diff --git a/apps/ui/src/components/operate/MonacoYaml.vue 
b/apps/ui/src/features/operate/_shared/MonacoYaml.vue
similarity index 99%
rename from apps/ui/src/components/operate/MonacoYaml.vue
rename to apps/ui/src/features/operate/_shared/MonacoYaml.vue
index 7313526..076d70f 100644
--- a/apps/ui/src/components/operate/MonacoYaml.vue
+++ b/apps/ui/src/features/operate/_shared/MonacoYaml.vue
@@ -18,7 +18,7 @@
 import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
 import * as monaco from 'monaco-editor';
 import type { Catalog } from '@skywalking-horizon-ui/api-client';
-import { setupMonaco, setModelCatalog, RR_THEME_NAME } from 
'../../monaco/setup.js';
+import { setupMonaco, setModelCatalog, RR_THEME_NAME } from 
'../../../monaco/setup.js';
 
 const props = defineProps<{
   modelValue: string;
diff --git a/apps/ui/src/components/operate/RuleCard.vue 
b/apps/ui/src/features/operate/_shared/RuleCard.vue
similarity index 100%
rename from apps/ui/src/components/operate/RuleCard.vue
rename to apps/ui/src/features/operate/_shared/RuleCard.vue
diff --git a/apps/ui/src/components/operate/grouping.ts 
b/apps/ui/src/features/operate/_shared/grouping.ts
similarity index 100%
rename from apps/ui/src/components/operate/grouping.ts
rename to apps/ui/src/features/operate/_shared/grouping.ts
diff --git a/apps/ui/src/views/operate/ClusterStatusView.vue 
b/apps/ui/src/features/operate/cluster/ClusterStatusView.vue
similarity index 99%
rename from apps/ui/src/views/operate/ClusterStatusView.vue
rename to apps/ui/src/features/operate/cluster/ClusterStatusView.vue
index 758496e..5785acc 100644
--- a/apps/ui/src/views/operate/ClusterStatusView.vue
+++ b/apps/ui/src/features/operate/cluster/ClusterStatusView.vue
@@ -16,8 +16,8 @@
 -->
 <script setup lang="ts">
 import { computed } from 'vue';
-import { useOapInfo } from '@/composables/useOapInfo';
-import { useAdminFeatures } from '@/composables/useAdminFeatures';
+import { useOapInfo } from '@/shell/useOapInfo';
+import { useAdminFeatures } from '@/shell/useAdminFeatures';
 
 // Two-pane Cluster Status:
 //   - Pane A (graphql / :12800): version, server clock, timezone,
diff --git a/apps/ui/src/views/operate/dsl/DslCatalogView.vue 
b/apps/ui/src/features/operate/dsl/DslCatalogView.vue
similarity index 98%
rename from apps/ui/src/views/operate/dsl/DslCatalogView.vue
rename to apps/ui/src/features/operate/dsl/DslCatalogView.vue
index 83c8e3c..99938b3 100644
--- a/apps/ui/src/views/operate/dsl/DslCatalogView.vue
+++ b/apps/ui/src/features/operate/dsl/DslCatalogView.vue
@@ -27,11 +27,11 @@ import {
   type ListRow,
 } from '@skywalking-horizon-ui/api-client';
 import { bff } from '@/api/client';
-import { groupRules } from '@/components/operate/grouping';
-import RuleCard from '@/components/operate/RuleCard.vue';
+import { groupRules } from '@/features/operate/_shared/grouping';
+import RuleCard from '@/features/operate/_shared/RuleCard.vue';
 import Pill from '@/components/primitives/Pill.vue';
 import Btn from '@/components/primitives/Btn.vue';
-import AdminFeatureWarning from '@/components/shell/AdminFeatureWarning.vue';
+import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
 
 const route = useRoute();
 const router = useRouter();
diff --git a/apps/ui/src/views/operate/dsl/DslDumpView.vue 
b/apps/ui/src/features/operate/dsl/DslDumpView.vue
similarity index 97%
rename from apps/ui/src/views/operate/dsl/DslDumpView.vue
rename to apps/ui/src/features/operate/dsl/DslDumpView.vue
index 518ebfd..490ff81 100644
--- a/apps/ui/src/views/operate/dsl/DslDumpView.vue
+++ b/apps/ui/src/features/operate/dsl/DslDumpView.vue
@@ -16,11 +16,11 @@
 -->
 <script setup lang="ts">
 import { CATALOGS, type Catalog } from '@skywalking-horizon-ui/api-client';
-import { useAuthStore } from '@/stores/auth';
+import { useAuthStore } from '@/state/auth';
 import { bff } from '@/api/client';
 import Btn from '@/components/primitives/Btn.vue';
 import Pill from '@/components/primitives/Pill.vue';
-import AdminFeatureWarning from '@/components/shell/AdminFeatureWarning.vue';
+import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
 
 const auth = useAuthStore();
 
diff --git a/apps/ui/src/views/operate/dsl/DslEditorView.vue 
b/apps/ui/src/features/operate/dsl/DslEditorView.vue
similarity index 97%
rename from apps/ui/src/views/operate/dsl/DslEditorView.vue
rename to apps/ui/src/features/operate/dsl/DslEditorView.vue
index 64e9157..b30d905 100644
--- a/apps/ui/src/views/operate/dsl/DslEditorView.vue
+++ b/apps/ui/src/features/operate/dsl/DslEditorView.vue
@@ -18,14 +18,14 @@
 import { computed, ref, shallowRef } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { isCatalog, type Catalog, type RuleResponse } from 
'@skywalking-horizon-ui/api-client';
-import { useAuthStore } from '@/stores/auth';
-import { useRuleEditor } from '@/composables/useRuleEditor';
+import { useAuthStore } from '@/state/auth';
+import { useRuleEditor } from '@/features/operate/dsl/useRuleEditor';
 import Btn from '@/components/primitives/Btn.vue';
 import Pill from '@/components/primitives/Pill.vue';
-import MonacoYaml from '@/components/operate/MonacoYaml.vue';
-import MonacoDiff from '@/components/operate/MonacoDiff.vue';
-import DestructiveConfirm from '@/components/operate/DestructiveConfirm.vue';
-import AdminFeatureWarning from '@/components/shell/AdminFeatureWarning.vue';
+import MonacoYaml from '@/features/operate/_shared/MonacoYaml.vue';
+import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import DestructiveConfirm from 
'@/features/operate/_shared/DestructiveConfirm.vue';
+import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
 
 const route = useRoute();
 const router = useRouter();
diff --git a/apps/ui/src/views/operate/dsl/OalCatalogView.vue 
b/apps/ui/src/features/operate/dsl/OalCatalogView.vue
similarity index 99%
rename from apps/ui/src/views/operate/dsl/OalCatalogView.vue
rename to apps/ui/src/features/operate/dsl/OalCatalogView.vue
index c4b1ba9..75d1466 100644
--- a/apps/ui/src/views/operate/dsl/OalCatalogView.vue
+++ b/apps/ui/src/features/operate/dsl/OalCatalogView.vue
@@ -34,7 +34,7 @@ import { bff } from '@/api/client';
 import Pill from '@/components/primitives/Pill.vue';
 import Btn from '@/components/primitives/Btn.vue';
 import { tokenizeLine, type Token } from './syntaxHighlight.js';
-import AdminFeatureWarning from '@/components/shell/AdminFeatureWarning.vue';
+import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
 
 const router = useRouter();
 
diff --git a/apps/ui/src/views/operate/dsl/syntaxHighlight.ts 
b/apps/ui/src/features/operate/dsl/syntaxHighlight.ts
similarity index 100%
rename from apps/ui/src/views/operate/dsl/syntaxHighlight.ts
rename to apps/ui/src/features/operate/dsl/syntaxHighlight.ts
diff --git a/apps/ui/src/composables/useRuleEditor.ts 
b/apps/ui/src/features/operate/dsl/useRuleEditor.ts
similarity index 100%
rename from apps/ui/src/composables/useRuleEditor.ts
rename to apps/ui/src/features/operate/dsl/useRuleEditor.ts
diff --git a/apps/ui/src/views/operate/InspectView.vue 
b/apps/ui/src/features/operate/inspect/InspectView.vue
similarity index 99%
rename from apps/ui/src/views/operate/InspectView.vue
rename to apps/ui/src/features/operate/inspect/InspectView.vue
index 98e9139..a5be58d 100644
--- a/apps/ui/src/views/operate/InspectView.vue
+++ b/apps/ui/src/features/operate/inspect/InspectView.vue
@@ -48,7 +48,7 @@ import {
 import { bff, describeApiError, type InspectCatalogEntry, type 
InspectServerTimeResponse } from '@/api/client';
 import Btn from '@/components/primitives/Btn.vue';
 import Pill from '@/components/primitives/Pill.vue';
-import AdminFeatureWarning from '@/components/shell/AdminFeatureWarning.vue';
+import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
 
 // ─── Types local to the page ────────────────────────────────────────
 
diff --git a/apps/ui/src/views/operate/live-debug/DebugHistoryView.vue 
b/apps/ui/src/features/operate/live-debug/DebugHistoryView.vue
similarity index 99%
rename from apps/ui/src/views/operate/live-debug/DebugHistoryView.vue
rename to apps/ui/src/features/operate/live-debug/DebugHistoryView.vue
index 9adf3ca..24ed3e3 100644
--- a/apps/ui/src/views/operate/live-debug/DebugHistoryView.vue
+++ b/apps/ui/src/features/operate/live-debug/DebugHistoryView.vue
@@ -33,7 +33,7 @@ import {
   useDebugHistory,
   type DebugWidget,
   type HistoryEntry,
-} from '@/composables/useDebugHistory';
+} from '@/features/operate/live-debug/useDebugHistory';
 import Pill from '@/components/primitives/Pill.vue';
 
 const router = useRouter();
diff --git a/apps/ui/src/views/operate/live-debug/DebugLal.vue 
b/apps/ui/src/features/operate/live-debug/DebugLal.vue
similarity index 99%
rename from apps/ui/src/views/operate/live-debug/DebugLal.vue
rename to apps/ui/src/features/operate/live-debug/DebugLal.vue
index b9708d7..df274c2 100644
--- a/apps/ui/src/views/operate/live-debug/DebugLal.vue
+++ b/apps/ui/src/features/operate/live-debug/DebugLal.vue
@@ -48,8 +48,8 @@ import type {
   SessionSample,
 } from '@skywalking-horizon-ui/api-client';
 import { bff } from '@/api/client';
-import { useDebugSession } from '@/composables/useDebugSession';
-import { useDebugHistory, type HistoryEntry } from 
'@/composables/useDebugHistory';
+import { useDebugSession } from 
'@/features/operate/live-debug/useDebugSession';
+import { useDebugHistory, type HistoryEntry } from 
'@/features/operate/live-debug/useDebugHistory';
 import Btn from '@/components/primitives/Btn.vue';
 import DebugView from './DebugView.vue';
 import { isLalSamplePayload } from './payload.js';
diff --git a/apps/ui/src/views/operate/live-debug/DebugMal.vue 
b/apps/ui/src/features/operate/live-debug/DebugMal.vue
similarity index 99%
rename from apps/ui/src/views/operate/live-debug/DebugMal.vue
rename to apps/ui/src/features/operate/live-debug/DebugMal.vue
index 1da7571..f7bea3a 100644
--- a/apps/ui/src/views/operate/live-debug/DebugMal.vue
+++ b/apps/ui/src/features/operate/live-debug/DebugMal.vue
@@ -42,8 +42,8 @@ import type {
   SessionSample,
 } from '@skywalking-horizon-ui/api-client';
 import { bff } from '@/api/client';
-import { useDebugSession } from '@/composables/useDebugSession';
-import { useDebugHistory, type HistoryEntry } from 
'@/composables/useDebugHistory';
+import { useDebugSession } from 
'@/features/operate/live-debug/useDebugSession';
+import { useDebugHistory, type HistoryEntry } from 
'@/features/operate/live-debug/useDebugHistory';
 import Btn from '@/components/primitives/Btn.vue';
 import DebugView from './DebugView.vue';
 import { isMalOutputPayload, isMalSamplesPayload, shortHash } from 
'./payload.js';
diff --git a/apps/ui/src/views/operate/live-debug/DebugOal.vue 
b/apps/ui/src/features/operate/live-debug/DebugOal.vue
similarity index 99%
rename from apps/ui/src/views/operate/live-debug/DebugOal.vue
rename to apps/ui/src/features/operate/live-debug/DebugOal.vue
index 16adb61..e2f7ba9 100644
--- a/apps/ui/src/views/operate/live-debug/DebugOal.vue
+++ b/apps/ui/src/features/operate/live-debug/DebugOal.vue
@@ -42,8 +42,8 @@ import type {
   SessionSample,
 } from '@skywalking-horizon-ui/api-client';
 import { bff } from '@/api/client';
-import { useDebugSession } from '@/composables/useDebugSession';
-import { useDebugHistory, type HistoryEntry } from 
'@/composables/useDebugHistory';
+import { useDebugSession } from 
'@/features/operate/live-debug/useDebugSession';
+import { useDebugHistory, type HistoryEntry } from 
'@/features/operate/live-debug/useDebugHistory';
 import Btn from '@/components/primitives/Btn.vue';
 import Pill from '@/components/primitives/Pill.vue';
 import DebugView from './DebugView.vue';
diff --git a/apps/ui/src/views/operate/live-debug/DebugView.vue 
b/apps/ui/src/features/operate/live-debug/DebugView.vue
similarity index 100%
rename from apps/ui/src/views/operate/live-debug/DebugView.vue
rename to apps/ui/src/features/operate/live-debug/DebugView.vue
diff --git a/apps/ui/src/views/operate/live-debug/LiveDebuggerView.vue 
b/apps/ui/src/features/operate/live-debug/LiveDebuggerView.vue
similarity index 98%
rename from apps/ui/src/views/operate/live-debug/LiveDebuggerView.vue
rename to apps/ui/src/features/operate/live-debug/LiveDebuggerView.vue
index f2840cb..5ff7a72 100644
--- a/apps/ui/src/views/operate/live-debug/LiveDebuggerView.vue
+++ b/apps/ui/src/features/operate/live-debug/LiveDebuggerView.vue
@@ -31,7 +31,7 @@ import { useRoute, useRouter } from 'vue-router';
 import DebugMal from './DebugMal.vue';
 import DebugLal from './DebugLal.vue';
 import DebugOal from './DebugOal.vue';
-import AdminFeatureWarning from '@/components/shell/AdminFeatureWarning.vue';
+import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
 
 type Tab = 'mal' | 'lal' | 'oal';
 
diff --git a/apps/ui/src/views/operate/live-debug/NodeCoverage.vue 
b/apps/ui/src/features/operate/live-debug/NodeCoverage.vue
similarity index 100%
rename from apps/ui/src/views/operate/live-debug/NodeCoverage.vue
rename to apps/ui/src/features/operate/live-debug/NodeCoverage.vue
diff --git a/apps/ui/src/views/operate/live-debug/constants.ts 
b/apps/ui/src/features/operate/live-debug/constants.ts
similarity index 100%
rename from apps/ui/src/views/operate/live-debug/constants.ts
rename to apps/ui/src/features/operate/live-debug/constants.ts
diff --git a/apps/ui/src/views/operate/live-debug/oalEntityId.ts 
b/apps/ui/src/features/operate/live-debug/oalEntityId.ts
similarity index 100%
rename from apps/ui/src/views/operate/live-debug/oalEntityId.ts
rename to apps/ui/src/features/operate/live-debug/oalEntityId.ts
diff --git a/apps/ui/src/views/operate/live-debug/payload.ts 
b/apps/ui/src/features/operate/live-debug/payload.ts
similarity index 100%
rename from apps/ui/src/views/operate/live-debug/payload.ts
rename to apps/ui/src/features/operate/live-debug/payload.ts
diff --git a/apps/ui/src/composables/useDebugHistory.ts 
b/apps/ui/src/features/operate/live-debug/useDebugHistory.ts
similarity index 100%
rename from apps/ui/src/composables/useDebugHistory.ts
rename to apps/ui/src/features/operate/live-debug/useDebugHistory.ts
diff --git a/apps/ui/src/composables/useDebugSession.ts 
b/apps/ui/src/features/operate/live-debug/useDebugSession.ts
similarity index 99%
rename from apps/ui/src/composables/useDebugSession.ts
rename to apps/ui/src/features/operate/live-debug/useDebugSession.ts
index 3e3c888..de5da5e 100644
--- a/apps/ui/src/composables/useDebugSession.ts
+++ b/apps/ui/src/features/operate/live-debug/useDebugSession.ts
@@ -44,7 +44,7 @@ import type {
   StartSessionArgs,
 } from '@skywalking-horizon-ui/api-client';
 import { bff, describeApiError } from '@/api/client';
-import { getClientId } from './useClientId.js';
+import { getClientId } from '../../../controls/clientId.js';
 
 export type DebugWidgetKey = 'mal' | 'lal' | 'oal';
 
diff --git a/apps/ui/src/views/setup/LayerSetupCard.vue 
b/apps/ui/src/features/setup/LayerSetupCard.vue
similarity index 99%
rename from apps/ui/src/views/setup/LayerSetupCard.vue
rename to apps/ui/src/features/setup/LayerSetupCard.vue
index 09e3026..0c0706a 100644
--- a/apps/ui/src/views/setup/LayerSetupCard.vue
+++ b/apps/ui/src/features/setup/LayerSetupCard.vue
@@ -18,7 +18,7 @@
 import { computed, ref } from 'vue';
 import type { LayerDef } from '@skywalking-horizon-ui/api-client';
 import Icon from '@/components/icons/Icon.vue';
-import { useSetupStore, defaultLandingFor } from '@/stores/setup';
+import { useSetupStore, defaultLandingFor } from '@/state/setup';
 
 const props = defineProps<{ layer: LayerDef; expanded?: boolean }>();
 const emit = defineEmits<{ (e: 'toggle'): void }>();
diff --git a/apps/ui/src/views/setup/SetupView.vue 
b/apps/ui/src/features/setup/SetupView.vue
similarity index 99%
rename from apps/ui/src/views/setup/SetupView.vue
rename to apps/ui/src/features/setup/SetupView.vue
index d23f088..1495fe5 100644
--- a/apps/ui/src/views/setup/SetupView.vue
+++ b/apps/ui/src/features/setup/SetupView.vue
@@ -32,7 +32,7 @@ import { computed, ref, watch } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
 import type { OverviewDashboard, OverviewWidget } from 
'@skywalking-horizon-ui/api-client';
 import { bffClient } from '@/api/client';
-import { useOverviewDashboards } from '@/composables/useOverviewDashboards';
+import { useOverviewDashboards } from 
'@/render/overview/useOverviewDashboards';
 
 const { isLoading, all, publicOverviews, operateOverviews } = 
useOverviewDashboards();
 
diff --git a/apps/ui/src/views/layer/LayerServiceSelector.vue 
b/apps/ui/src/layer/LayerServiceSelector.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerServiceSelector.vue
rename to apps/ui/src/layer/LayerServiceSelector.vue
index 9f5d2dc..17de248 100644
--- a/apps/ui/src/views/layer/LayerServiceSelector.vue
+++ b/apps/ui/src/layer/LayerServiceSelector.vue
@@ -25,8 +25,8 @@
 <script setup lang="ts">
 import { computed, ref, watch } from 'vue';
 import type { LandingColumn, LandingServiceRow } from 
'@skywalking-horizon-ui/api-client';
-import { metricMeta } from '@/composables/metricCatalog';
-import { statusForMetrics, thresholdColor } from '@/composables/metricColor';
+import { metricMeta } from '@/utils/metricCatalog';
+import { statusForMetrics, thresholdColor } from '@/utils/metricColor';
 import { fmtMetric } from '@/utils/formatters';
 import { resolveServiceIdentity } from '@/utils/serviceName';
 import type { ServiceNamingRule } from '@skywalking-horizon-ui/api-client';
diff --git a/apps/ui/src/views/layer/LayerShell.vue 
b/apps/ui/src/layer/LayerShell.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerShell.vue
rename to apps/ui/src/layer/LayerShell.vue
index ee2c84f..f568b00 100644
--- a/apps/ui/src/views/layer/LayerShell.vue
+++ b/apps/ui/src/layer/LayerShell.vue
@@ -32,12 +32,12 @@ import type { LayerDef } from 
'@skywalking-horizon-ui/api-client';
 import Icon from '@/components/icons/Icon.vue';
 import Sparkline from '@/components/charts/Sparkline.vue';
 import LayerServiceSelector from './LayerServiceSelector.vue';
-import { metricMeta } from '@/composables/metricCatalog';
-import { colorForMetric } from '@/composables/metricColor';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useLayers, firstLayerTab } from '@/composables/useLayers';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useSetupStore } from '@/stores/setup';
+import { metricMeta } from '@/utils/metricCatalog';
+import { colorForMetric } from '@/utils/metricColor';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useLayers, firstLayerTab } from '@/shell/useLayers';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useSetupStore } from '@/state/setup';
 import { fmtMetric } from '@/utils/formatters';
 import { parseServiceName } from '@/utils/serviceName';
 
diff --git a/apps/ui/src/views/layer/LayerTabPlaceholder.vue 
b/apps/ui/src/layer/LayerTabPlaceholder.vue
similarity index 97%
rename from apps/ui/src/views/layer/LayerTabPlaceholder.vue
rename to apps/ui/src/layer/LayerTabPlaceholder.vue
index d9f0bed..dbbf887 100644
--- a/apps/ui/src/views/layer/LayerTabPlaceholder.vue
+++ b/apps/ui/src/layer/LayerTabPlaceholder.vue
@@ -23,7 +23,7 @@
 -->
 <script setup lang="ts">
 import { computed } from 'vue';
-import { useSelectedService } from '@/composables/useSelectedService';
+import { useSelectedService } from '@/layer/useSelectedService';
 
 defineProps<{ title: string; phase: string; note?: string }>();
 
diff --git a/apps/ui/src/views/layer/LayerEndpointDependencyView.vue 
b/apps/ui/src/layer/endpoint-dependency/LayerEndpointDependencyView.vue
similarity index 99%
rename from apps/ui/src/views/layer/LayerEndpointDependencyView.vue
rename to apps/ui/src/layer/endpoint-dependency/LayerEndpointDependencyView.vue
index f225ca7..90a72d6 100644
--- a/apps/ui/src/views/layer/LayerEndpointDependencyView.vue
+++ b/apps/ui/src/layer/endpoint-dependency/LayerEndpointDependencyView.vue
@@ -41,13 +41,13 @@ import type {
   TopologyMetricDef,
 } from '@/api/client';
 import { bffClient } from '@/api/client';
-import { useLayerEndpointDependency } from 
'@/composables/useLayerEndpointDependency';
-import { useLayerEndpoints } from '@/composables/useLayerEndpoints';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useLayers } from '@/composables/useLayers';
-import { useSelectedEndpoint } from '@/composables/useSelectedEndpoint';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useSetupStore } from '@/stores/setup';
+import { useLayerEndpointDependency } from 
'@/layer/endpoint-dependency/useLayerEndpointDependency';
+import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useLayers } from '@/shell/useLayers';
+import { useSelectedEndpoint } from '@/layer/useSelectedEndpoint';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useSetupStore } from '@/state/setup';
 import { fmtMetric } from '@/utils/formatters';
 import { resolveServiceIdentity, type ServiceIdentity } from 
'@/utils/serviceName';
 import { watch } from 'vue';
diff --git a/apps/ui/src/composables/useLayerEndpointDependency.ts 
b/apps/ui/src/layer/endpoint-dependency/useLayerEndpointDependency.ts
similarity index 95%
rename from apps/ui/src/composables/useLayerEndpointDependency.ts
rename to apps/ui/src/layer/endpoint-dependency/useLayerEndpointDependency.ts
index 47bfcfd..0116444 100644
--- a/apps/ui/src/composables/useLayerEndpointDependency.ts
+++ b/apps/ui/src/layer/endpoint-dependency/useLayerEndpointDependency.ts
@@ -23,7 +23,7 @@
 
 import { computed, type Ref } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from 
'../../controls/useAutoRefreshSubscribe';
 import { bffClient } from '@/api/client';
 
 export function useLayerEndpointDependency(
diff --git a/apps/ui/src/views/layer/LayerLogsView.vue 
b/apps/ui/src/layer/logs/LayerLogsView.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerLogsView.vue
rename to apps/ui/src/layer/logs/LayerLogsView.vue
index 3fe9b8d..7b18f66 100644
--- a/apps/ui/src/views/layer/LayerLogsView.vue
+++ b/apps/ui/src/layer/logs/LayerLogsView.vue
@@ -28,17 +28,17 @@
 import { computed, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
 import type { LayerDef, LogRow, LogTagFilter } from '@/api/client';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useLayerLogs, useLayerLogFacets } from '@/composables/useLayerLogs';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useLayerLogs, useLayerLogFacets } from '@/layer/logs/useLayerLogs';
 import { bffClient } from '@/api/client';
-import { useLayerInstances } from '@/composables/useLayerInstances';
-import { useLayerEndpoints } from '@/composables/useLayerEndpoints';
-import { useLayers } from '@/composables/useLayers';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useSelectedInstance } from '@/composables/useSelectedInstance';
-import { useSelectedEndpoint } from '@/composables/useSelectedEndpoint';
-import { useSetupStore } from '@/stores/setup';
-import { useTracePopout } from '@/composables/useTracePopout';
+import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
+import { useLayers } from '@/shell/useLayers';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useSelectedInstance } from '@/layer/useSelectedInstance';
+import { useSelectedEndpoint } from '@/layer/useSelectedEndpoint';
+import { useSetupStore } from '@/state/setup';
+import { useTracePopout } from '@/layer/traces/useTracePopout';
 import { parseServiceName } from '@/utils/serviceName';
 
 const route = useRoute();
diff --git a/apps/ui/src/composables/useLayerLogs.ts 
b/apps/ui/src/layer/logs/useLayerLogs.ts
similarity index 98%
rename from apps/ui/src/composables/useLayerLogs.ts
rename to apps/ui/src/layer/logs/useLayerLogs.ts
index 7358adf..4c03312 100644
--- a/apps/ui/src/composables/useLayerLogs.ts
+++ b/apps/ui/src/layer/logs/useLayerLogs.ts
@@ -17,7 +17,7 @@
 
 import { computed, type Ref } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from 
'../../controls/useAutoRefreshSubscribe';
 import { bffClient } from '@/api/client';
 import type { LogFacetsResponse, LogsResponse, LogTagFilter } from 
'@/api/client';
 
diff --git a/apps/ui/src/views/layer/LayerAsyncProfilingView.vue 
b/apps/ui/src/layer/profiling/LayerAsyncProfilingView.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerAsyncProfilingView.vue
rename to apps/ui/src/layer/profiling/LayerAsyncProfilingView.vue
index 83890f6..a3b6193 100644
--- a/apps/ui/src/views/layer/LayerAsyncProfilingView.vue
+++ b/apps/ui/src/layer/profiling/LayerAsyncProfilingView.vue
@@ -26,8 +26,8 @@
 <script setup lang="ts">
 import { computed, reactive, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
-import { useLayerInstances } from '@/composables/useLayerInstances';
-import { useSelectedService } from '@/composables/useSelectedService';
+import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useSelectedService } from '@/layer/useSelectedService';
 import { bffClient } from '@/api/client';
 import type {
   AsyncProfilingEvent,
@@ -35,7 +35,7 @@ import type {
   AsyncProfilingTree,
   ProfileAnalyzationTree,
 } from '@/api/client';
-import ProfileFlameGraph from '@/components/profile/ProfileFlameGraph.vue';
+import ProfileFlameGraph from '@/layer/profiling/ProfileFlameGraph.vue';
 
 const route = useRoute();
 const layerKey = computed(() => String(route.params.layerKey ?? ''));
diff --git a/apps/ui/src/views/layer/LayerEBPFProfilingView.vue 
b/apps/ui/src/layer/profiling/LayerEBPFProfilingView.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerEBPFProfilingView.vue
rename to apps/ui/src/layer/profiling/LayerEBPFProfilingView.vue
index c681a61..5e0bf0c 100644
--- a/apps/ui/src/views/layer/LayerEBPFProfilingView.vue
+++ b/apps/ui/src/layer/profiling/LayerEBPFProfilingView.vue
@@ -30,10 +30,10 @@
 <script setup lang="ts">
 import { computed, reactive, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
-import { useLayers } from '@/composables/useLayers';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useSetupStore } from '@/stores/setup';
+import { useLayers } from '@/shell/useLayers';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useSetupStore } from '@/state/setup';
 import { bffClient } from '@/api/client';
 import type {
   EBPFAnalysisTree,
@@ -44,8 +44,8 @@ import type {
   LayerDef,
   ProfileAnalyzationTree,
 } from '@/api/client';
-import ProfileFlameGraph from '@/components/profile/ProfileFlameGraph.vue';
-import ProfileStackTable from '@/components/profile/ProfileStackTable.vue';
+import ProfileFlameGraph from '@/layer/profiling/ProfileFlameGraph.vue';
+import ProfileStackTable from '@/layer/profiling/ProfileStackTable.vue';
 
 const route = useRoute();
 const layerKey = computed(() => String(route.params.layerKey ?? ''));
diff --git a/apps/ui/src/views/layer/LayerNetworkProfilingView.vue 
b/apps/ui/src/layer/profiling/LayerNetworkProfilingView.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerNetworkProfilingView.vue
rename to apps/ui/src/layer/profiling/LayerNetworkProfilingView.vue
index 476e984..32f46c6 100644
--- a/apps/ui/src/views/layer/LayerNetworkProfilingView.vue
+++ b/apps/ui/src/layer/profiling/LayerNetworkProfilingView.vue
@@ -33,8 +33,8 @@
 <script setup lang="ts">
 import { computed, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
-import { useLayerInstances } from '@/composables/useLayerInstances';
-import { useSelectedService } from '@/composables/useSelectedService';
+import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useSelectedService } from '@/layer/useSelectedService';
 import { bffClient } from '@/api/client';
 import type {
   EBPFTask,
@@ -42,7 +42,7 @@ import type {
   ProcessCall,
   ProcessNode,
 } from '@/api/client';
-import ProcessTopologyGraph from 
'@/components/profile/ProcessTopologyGraph.vue';
+import ProcessTopologyGraph from '@/layer/profiling/ProcessTopologyGraph.vue';
 
 const route = useRoute();
 const layerKey = computed(() => String(route.params.layerKey ?? ''));
diff --git a/apps/ui/src/views/layer/LayerPprofProfilingView.vue 
b/apps/ui/src/layer/profiling/LayerPprofProfilingView.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerPprofProfilingView.vue
rename to apps/ui/src/layer/profiling/LayerPprofProfilingView.vue
index 7dbc754..5018848 100644
--- a/apps/ui/src/views/layer/LayerPprofProfilingView.vue
+++ b/apps/ui/src/layer/profiling/LayerPprofProfilingView.vue
@@ -26,11 +26,11 @@
 <script setup lang="ts">
 import { computed, reactive, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
-import { useLayerInstances } from '@/composables/useLayerInstances';
-import { useSelectedService } from '@/composables/useSelectedService';
+import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useSelectedService } from '@/layer/useSelectedService';
 import { bffClient } from '@/api/client';
 import type { PprofTask, PprofTree, ProfileAnalyzationTree } from 
'@/api/client';
-import ProfileFlameGraph from '@/components/profile/ProfileFlameGraph.vue';
+import ProfileFlameGraph from '@/layer/profiling/ProfileFlameGraph.vue';
 
 const route = useRoute();
 const layerKey = computed(() => String(route.params.layerKey ?? ''));
diff --git a/apps/ui/src/views/layer/LayerTraceProfilingView.vue 
b/apps/ui/src/layer/profiling/LayerTraceProfilingView.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerTraceProfilingView.vue
rename to apps/ui/src/layer/profiling/LayerTraceProfilingView.vue
index 821fa00..e67f041 100644
--- a/apps/ui/src/views/layer/LayerTraceProfilingView.vue
+++ b/apps/ui/src/layer/profiling/LayerTraceProfilingView.vue
@@ -36,11 +36,11 @@
 <script setup lang="ts">
 import { computed, reactive, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
-import { useLayers } from '@/composables/useLayers';
-import { useLayerEndpoints } from '@/composables/useLayerEndpoints';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useSetupStore } from '@/stores/setup';
+import { useLayers } from '@/shell/useLayers';
+import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useSetupStore } from '@/state/setup';
 import { bffClient } from '@/api/client';
 import type {
   LayerDef,
@@ -52,9 +52,9 @@ import type {
   ProfileTaskLog,
   ProfileTimeRange,
 } from '@/api/client';
-import ProfileStackTable from '@/components/profile/ProfileStackTable.vue';
-import ProfileFlameGraph from '@/components/profile/ProfileFlameGraph.vue';
-import NativeTraceWaterfall from '@/components/trace/NativeTraceWaterfall.vue';
+import ProfileStackTable from '@/layer/profiling/ProfileStackTable.vue';
+import ProfileFlameGraph from '@/layer/profiling/ProfileFlameGraph.vue';
+import NativeTraceWaterfall from '@/layer/traces/NativeTraceWaterfall.vue';
 
 const route = useRoute();
 const layerKey = computed(() => String(route.params.layerKey ?? ''));
diff --git a/apps/ui/src/components/profile/ProcessTopologyGraph.vue 
b/apps/ui/src/layer/profiling/ProcessTopologyGraph.vue
similarity index 100%
rename from apps/ui/src/components/profile/ProcessTopologyGraph.vue
rename to apps/ui/src/layer/profiling/ProcessTopologyGraph.vue
diff --git a/apps/ui/src/components/profile/ProfileFlameGraph.vue 
b/apps/ui/src/layer/profiling/ProfileFlameGraph.vue
similarity index 100%
rename from apps/ui/src/components/profile/ProfileFlameGraph.vue
rename to apps/ui/src/layer/profiling/ProfileFlameGraph.vue
diff --git a/apps/ui/src/components/profile/ProfileStackRow.vue 
b/apps/ui/src/layer/profiling/ProfileStackRow.vue
similarity index 100%
rename from apps/ui/src/components/profile/ProfileStackRow.vue
rename to apps/ui/src/layer/profiling/ProfileStackRow.vue
diff --git a/apps/ui/src/components/profile/ProfileStackTable.vue 
b/apps/ui/src/layer/profiling/ProfileStackTable.vue
similarity index 100%
rename from apps/ui/src/components/profile/ProfileStackTable.vue
rename to apps/ui/src/layer/profiling/ProfileStackTable.vue
diff --git a/apps/ui/src/views/layer/LayerServiceMapView.vue 
b/apps/ui/src/layer/service-map/LayerServiceMapView.vue
similarity index 99%
rename from apps/ui/src/views/layer/LayerServiceMapView.vue
rename to apps/ui/src/layer/service-map/LayerServiceMapView.vue
index 8929fa4..a6123a5 100644
--- a/apps/ui/src/views/layer/LayerServiceMapView.vue
+++ b/apps/ui/src/layer/service-map/LayerServiceMapView.vue
@@ -60,17 +60,17 @@ import type {
   TopologyMetricDef,
   TopologyNode,
 } from '@/api/client';
-import { useLayerTopology } from '@/composables/useLayerTopology';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useLayers } from '@/composables/useLayers';
-import { useSetupStore } from '@/stores/setup';
+import { useLayerTopology } from '@/layer/service-map/useLayerTopology';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useLayers } from '@/shell/useLayers';
+import { useSetupStore } from '@/state/setup';
 import { fmtMetric } from '@/utils/formatters';
 import {
   resolveServiceIdentity,
   type ServiceIdentity,
 } from '@/utils/serviceName';
 import Sparkline from '@/components/charts/Sparkline.vue';
-import { isUserNode } from '@/composables/useTopologyIcons';
+import { isUserNode } from '@/layer/service-map/useTopologyIcons';
 
 /** When embedded as a widget (e.g. inside the Services / Mesh overview
  *  dashboards) the host passes the layer key directly and asks for the
diff --git a/apps/ui/src/composables/useLayerTopology.ts 
b/apps/ui/src/layer/service-map/useLayerTopology.ts
similarity index 95%
rename from apps/ui/src/composables/useLayerTopology.ts
rename to apps/ui/src/layer/service-map/useLayerTopology.ts
index 0add8ad..a9d1944 100644
--- a/apps/ui/src/composables/useLayerTopology.ts
+++ b/apps/ui/src/layer/service-map/useLayerTopology.ts
@@ -23,7 +23,7 @@
 
 import { computed, type Ref } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from 
'../../controls/useAutoRefreshSubscribe';
 import { bffClient } from '@/api/client';
 
 export function useLayerTopology(
diff --git a/apps/ui/src/composables/useTopologyIcons.ts 
b/apps/ui/src/layer/service-map/useTopologyIcons.ts
similarity index 100%
rename from apps/ui/src/composables/useTopologyIcons.ts
rename to apps/ui/src/layer/service-map/useTopologyIcons.ts
diff --git a/apps/ui/src/views/layer/LayerTracesEntry.vue 
b/apps/ui/src/layer/traces/LayerTracesEntry.vue
similarity index 98%
rename from apps/ui/src/views/layer/LayerTracesEntry.vue
rename to apps/ui/src/layer/traces/LayerTracesEntry.vue
index 98ba7a8..e5dcca6 100644
--- a/apps/ui/src/views/layer/LayerTracesEntry.vue
+++ b/apps/ui/src/layer/traces/LayerTracesEntry.vue
@@ -30,7 +30,7 @@
 import { computed, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
 import type { LayerDef } from '@skywalking-horizon-ui/api-client';
-import { useLayers } from '@/composables/useLayers';
+import { useLayers } from '@/shell/useLayers';
 import LayerTracesView from './LayerTracesView.vue';
 import LayerZipkinTracesView from './LayerZipkinTracesView.vue';
 
diff --git a/apps/ui/src/views/layer/LayerTracesView.vue 
b/apps/ui/src/layer/traces/LayerTracesView.vue
similarity index 99%
rename from apps/ui/src/views/layer/LayerTracesView.vue
rename to apps/ui/src/layer/traces/LayerTracesView.vue
index 48f96c8..d3ee54a 100644
--- a/apps/ui/src/views/layer/LayerTracesView.vue
+++ b/apps/ui/src/layer/traces/LayerTracesView.vue
@@ -44,15 +44,15 @@ import type {
   TraceQueryOrder,
   TraceQueryState,
 } from '@/api/client';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useLayers } from '@/composables/useLayers';
-import { useLayerTraces, useTraceDetail } from '@/composables/useLayerTraces';
-import { useLayerInstances } from '@/composables/useLayerInstances';
-import { useLayerEndpoints } from '@/composables/useLayerEndpoints';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useSetupStore } from '@/stores/setup';
-import { useTracePopout } from '@/composables/useTracePopout';
-import { componentIconOrNull } from '@/composables/useTopologyIcons';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useLayers } from '@/shell/useLayers';
+import { useLayerTraces, useTraceDetail } from '@/layer/traces/useLayerTraces';
+import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useSetupStore } from '@/state/setup';
+import { useTracePopout } from '@/layer/traces/useTracePopout';
+import { componentIconOrNull } from '@/layer/service-map/useTopologyIcons';
 import { fmtMetric } from '@/utils/formatters';
 import { bffClient } from '@/api/client';
 import * as d3 from 'd3';
diff --git a/apps/ui/src/views/layer/LayerZipkinTracesView.vue 
b/apps/ui/src/layer/traces/LayerZipkinTracesView.vue
similarity index 99%
rename from apps/ui/src/views/layer/LayerZipkinTracesView.vue
rename to apps/ui/src/layer/traces/LayerZipkinTracesView.vue
index 1227e20..36fa459 100644
--- a/apps/ui/src/views/layer/LayerZipkinTracesView.vue
+++ b/apps/ui/src/layer/traces/LayerZipkinTracesView.vue
@@ -25,8 +25,8 @@
 import { computed, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
 import type { ZipkinTraceListRow } from '@skywalking-horizon-ui/api-client';
-import { useLayerZipkinTraces, useZipkinTrace } from 
'@/composables/useZipkinTraces';
-import { useZipkinTracePopout } from '@/composables/useZipkinTracePopout';
+import { useLayerZipkinTraces, useZipkinTrace } from 
'@/layer/traces/useZipkinTraces';
+import { useZipkinTracePopout } from '@/layer/traces/useZipkinTracePopout';
 import { bffClient } from '@/api/client';
 
 // Zipkin trace data is keyed by its own service universe (the names
diff --git a/apps/ui/src/components/trace/NativeTraceWaterfall.vue 
b/apps/ui/src/layer/traces/NativeTraceWaterfall.vue
similarity index 99%
rename from apps/ui/src/components/trace/NativeTraceWaterfall.vue
rename to apps/ui/src/layer/traces/NativeTraceWaterfall.vue
index c49d41b..ecfbebd 100644
--- a/apps/ui/src/components/trace/NativeTraceWaterfall.vue
+++ b/apps/ui/src/layer/traces/NativeTraceWaterfall.vue
@@ -32,7 +32,7 @@
 -->
 <script setup lang="ts">
 import { computed } from 'vue';
-import { componentIconOrNull } from '@/composables/useTopologyIcons';
+import { componentIconOrNull } from '@/layer/service-map/useTopologyIcons';
 import { fmtMetric } from '@/utils/formatters';
 
 /** Structural type covering both NativeSpan (from /trace) and
diff --git a/apps/ui/src/components/trace/TracePopout.vue 
b/apps/ui/src/layer/traces/TracePopout.vue
similarity index 99%
rename from apps/ui/src/components/trace/TracePopout.vue
rename to apps/ui/src/layer/traces/TracePopout.vue
index 40fb6c4..ae6c4c3 100644
--- a/apps/ui/src/components/trace/TracePopout.vue
+++ b/apps/ui/src/layer/traces/TracePopout.vue
@@ -31,9 +31,9 @@
 <script setup lang="ts">
 import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
 import type { NativeSpan, TraceAttachedEvent, TraceLogEntry } from 
'@/api/client';
-import { useTraceDetail } from '@/composables/useLayerTraces';
-import { useTracePopout } from '@/composables/useTracePopout';
-import { componentIconOrNull } from '@/composables/useTopologyIcons';
+import { useTraceDetail } from '@/layer/traces/useLayerTraces';
+import { useTracePopout } from '@/layer/traces/useTracePopout';
+import { componentIconOrNull } from '@/layer/service-map/useTopologyIcons';
 import { fmtMetric } from '@/utils/formatters';
 
 const { openTraceId, openTrace, closeTrace } = useTracePopout();
diff --git a/apps/ui/src/components/trace/ZipkinTracePopout.vue 
b/apps/ui/src/layer/traces/ZipkinTracePopout.vue
similarity index 99%
rename from apps/ui/src/components/trace/ZipkinTracePopout.vue
rename to apps/ui/src/layer/traces/ZipkinTracePopout.vue
index a6bba11..e601bf9 100644
--- a/apps/ui/src/components/trace/ZipkinTracePopout.vue
+++ b/apps/ui/src/layer/traces/ZipkinTracePopout.vue
@@ -31,8 +31,8 @@
 <script setup lang="ts">
 import { computed, ref, watch } from 'vue';
 import type { ZipkinSpan } from '@skywalking-horizon-ui/api-client';
-import { useZipkinTracePopout } from '@/composables/useZipkinTracePopout';
-import { useZipkinTrace } from '@/composables/useZipkinTraces';
+import { useZipkinTracePopout } from '@/layer/traces/useZipkinTracePopout';
+import { useZipkinTrace } from '@/layer/traces/useZipkinTraces';
 
 const { openTraceId, closeTrace } = useZipkinTracePopout();
 const traceIdRef = computed(() => openTraceId.value);
diff --git a/apps/ui/src/composables/useLayerTraces.ts 
b/apps/ui/src/layer/traces/useLayerTraces.ts
similarity index 100%
rename from apps/ui/src/composables/useLayerTraces.ts
rename to apps/ui/src/layer/traces/useLayerTraces.ts
diff --git a/apps/ui/src/composables/useTracePopout.ts 
b/apps/ui/src/layer/traces/useTracePopout.ts
similarity index 100%
rename from apps/ui/src/composables/useTracePopout.ts
rename to apps/ui/src/layer/traces/useTracePopout.ts
diff --git a/apps/ui/src/composables/useZipkinTracePopout.ts 
b/apps/ui/src/layer/traces/useZipkinTracePopout.ts
similarity index 100%
rename from apps/ui/src/composables/useZipkinTracePopout.ts
rename to apps/ui/src/layer/traces/useZipkinTracePopout.ts
diff --git a/apps/ui/src/composables/useZipkinTraces.ts 
b/apps/ui/src/layer/traces/useZipkinTraces.ts
similarity index 100%
rename from apps/ui/src/composables/useZipkinTraces.ts
rename to apps/ui/src/layer/traces/useZipkinTraces.ts
diff --git a/apps/ui/src/composables/useLayerEndpoints.ts 
b/apps/ui/src/layer/useLayerEndpoints.ts
similarity index 100%
rename from apps/ui/src/composables/useLayerEndpoints.ts
rename to apps/ui/src/layer/useLayerEndpoints.ts
diff --git a/apps/ui/src/composables/useLayerInstances.ts 
b/apps/ui/src/layer/useLayerInstances.ts
similarity index 100%
rename from apps/ui/src/composables/useLayerInstances.ts
rename to apps/ui/src/layer/useLayerInstances.ts
diff --git a/apps/ui/src/composables/useLayerLanding.ts 
b/apps/ui/src/layer/useLayerLanding.ts
similarity index 97%
rename from apps/ui/src/composables/useLayerLanding.ts
rename to apps/ui/src/layer/useLayerLanding.ts
index 054f6da..b22b199 100644
--- a/apps/ui/src/composables/useLayerLanding.ts
+++ b/apps/ui/src/layer/useLayerLanding.ts
@@ -17,7 +17,7 @@
 
 import { computed, type Ref } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from '../controls/useAutoRefreshSubscribe';
 import type { LandingConfig, LandingResponse, LayerDef } from 
'@skywalking-horizon-ui/api-client';
 import { bffClient } from '@/api/client';
 
diff --git a/apps/ui/src/composables/useSelectedEndpoint.ts 
b/apps/ui/src/layer/useSelectedEndpoint.ts
similarity index 100%
rename from apps/ui/src/composables/useSelectedEndpoint.ts
rename to apps/ui/src/layer/useSelectedEndpoint.ts
diff --git a/apps/ui/src/composables/useSelectedInstance.ts 
b/apps/ui/src/layer/useSelectedInstance.ts
similarity index 100%
rename from apps/ui/src/composables/useSelectedInstance.ts
rename to apps/ui/src/layer/useSelectedInstance.ts
diff --git a/apps/ui/src/composables/useSelectedService.ts 
b/apps/ui/src/layer/useSelectedService.ts
similarity index 100%
rename from apps/ui/src/composables/useSelectedService.ts
rename to apps/ui/src/layer/useSelectedService.ts
diff --git a/apps/ui/src/main.ts b/apps/ui/src/main.ts
index 0025101..d999315 100644
--- a/apps/ui/src/main.ts
+++ b/apps/ui/src/main.ts
@@ -19,9 +19,9 @@ import { createPinia } from 'pinia';
 import { VueQueryPlugin, QueryClient } from '@tanstack/vue-query';
 
 import App from './App.vue';
-import router from './router';
+import router from './shell/router/index';
 import { bffClient } from './api/client';
-import { useAuthStore } from './stores/auth';
+import { useAuthStore } from './state/auth';
 
 import '@skywalking-horizon-ui/design-tokens/tokens.css';
 import './assets/styles/global.css';
diff --git a/apps/ui/src/views/layer/LayerDashboardsView.vue 
b/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
similarity index 97%
rename from apps/ui/src/views/layer/LayerDashboardsView.vue
rename to apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
index 2d54ca9..800ab83 100644
--- a/apps/ui/src/views/layer/LayerDashboardsView.vue
+++ b/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
@@ -31,16 +31,16 @@ import { useRoute } from 'vue-router';
 import type { LayerDef } from '@skywalking-horizon-ui/api-client';
 import TimeChart from '@/components/charts/TimeChart.vue';
 import TopList from '@/components/charts/TopList.vue';
-import { colorForMetric } from '@/composables/metricColor';
-import { useLayerDashboard, useLayerDashboardConfig } from 
'@/composables/useLayerDashboard';
-import { useLayerEndpoints } from '@/composables/useLayerEndpoints';
-import { useLayerInstances } from '@/composables/useLayerInstances';
-import { useLayerLanding } from '@/composables/useLayerLanding';
-import { useLayers } from '@/composables/useLayers';
-import { useSelectedEndpoint } from '@/composables/useSelectedEndpoint';
-import { useSelectedInstance } from '@/composables/useSelectedInstance';
-import { useSelectedService } from '@/composables/useSelectedService';
-import { useSetupStore } from '@/stores/setup';
+import { colorForMetric } from '@/utils/metricColor';
+import { useLayerDashboard, useLayerDashboardConfig } from 
'@/render/layer-dashboard/useLayerDashboard';
+import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
+import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useLayerLanding } from '@/layer/useLayerLanding';
+import { useLayers } from '@/shell/useLayers';
+import { useSelectedEndpoint } from '@/layer/useSelectedEndpoint';
+import { useSelectedInstance } from '@/layer/useSelectedInstance';
+import { useSelectedService } from '@/layer/useSelectedService';
+import { useSetupStore } from '@/state/setup';
 import { fmtMetricAs } from '@/utils/formatters';
 import { ref, watch, watchEffect } from 'vue';
 
diff --git a/apps/ui/src/composables/useLayerDashboard.ts 
b/apps/ui/src/render/layer-dashboard/useLayerDashboard.ts
similarity index 98%
rename from apps/ui/src/composables/useLayerDashboard.ts
rename to apps/ui/src/render/layer-dashboard/useLayerDashboard.ts
index 7c17a55..0c6f449 100644
--- a/apps/ui/src/composables/useLayerDashboard.ts
+++ b/apps/ui/src/render/layer-dashboard/useLayerDashboard.ts
@@ -29,7 +29,7 @@
 
 import { computed, type Ref } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from 
'../../controls/useAutoRefreshSubscribe';
 import { bffClient } from '@/api/client';
 
 export function useLayerDashboardConfig(layerKey: Ref<string>, scope?: 
Ref<string>) {
diff --git a/apps/ui/src/views/overview/OverviewDashboardView.vue 
b/apps/ui/src/render/overview/OverviewDashboardView.vue
similarity index 90%
rename from apps/ui/src/views/overview/OverviewDashboardView.vue
rename to apps/ui/src/render/overview/OverviewDashboardView.vue
index 7d2512b..7e4cc47 100644
--- a/apps/ui/src/views/overview/OverviewDashboardView.vue
+++ b/apps/ui/src/render/overview/OverviewDashboardView.vue
@@ -29,15 +29,15 @@
 import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 import type { OverviewWidget } from '@skywalking-horizon-ui/api-client';
-import { useOverviewDashboard } from '@/composables/useOverviewDashboard';
-import SectionBreak from '@/components/overview/widgets/SectionBreak.vue';
-import ServiceCountWidget from 
'@/components/overview/widgets/ServiceCountWidget.vue';
-import MetricWidget from '@/components/overview/widgets/MetricWidget.vue';
-import KpiTileWidget from '@/components/overview/widgets/KpiTileWidget.vue';
-import AlarmsWidget from '@/components/overview/widgets/AlarmsWidget.vue';
-import K8sSummaryWidget from 
'@/components/overview/widgets/K8sSummaryWidget.vue';
-import PilotSummaryWidget from 
'@/components/overview/widgets/PilotSummaryWidget.vue';
-import LayerServiceMapView from '@/views/layer/LayerServiceMapView.vue';
+import { useOverviewDashboard } from '@/render/overview/useOverviewDashboard';
+import SectionBreak from '@/render/widgets/SectionBreak.vue';
+import ServiceCountWidget from '@/render/widgets/ServiceCountWidget.vue';
+import MetricWidget from '@/render/widgets/MetricWidget.vue';
+import KpiTileWidget from '@/render/widgets/KpiTileWidget.vue';
+import AlarmsWidget from '@/render/widgets/AlarmsWidget.vue';
+import K8sSummaryWidget from '@/render/widgets/K8sSummaryWidget.vue';
+import PilotSummaryWidget from '@/render/widgets/PilotSummaryWidget.vue';
+import LayerServiceMapView from '@/layer/service-map/LayerServiceMapView.vue';
 
 const route = useRoute();
 const dashId = computed(() => String(route.params.id ?? ''));
diff --git a/apps/ui/src/views/overview/OverviewLanding.vue 
b/apps/ui/src/render/overview/OverviewLanding.vue
similarity index 96%
rename from apps/ui/src/views/overview/OverviewLanding.vue
rename to apps/ui/src/render/overview/OverviewLanding.vue
index 3e679a6..7ddbb78 100644
--- a/apps/ui/src/views/overview/OverviewLanding.vue
+++ b/apps/ui/src/render/overview/OverviewLanding.vue
@@ -23,8 +23,8 @@
 <script setup lang="ts">
 import { watchEffect } from 'vue';
 import { RouterLink, useRouter } from 'vue-router';
-import { useOverviewDashboards } from '@/composables/useOverviewDashboards';
-import { useLayers } from '@/composables/useLayers';
+import { useOverviewDashboards } from 
'@/render/overview/useOverviewDashboards';
+import { useLayers } from '@/shell/useLayers';
 
 const router = useRouter();
 const { publicOverviews, isLoading } = useOverviewDashboards();
diff --git a/apps/ui/src/composables/useOverviewDashboard.ts 
b/apps/ui/src/render/overview/useOverviewDashboard.ts
similarity index 100%
rename from apps/ui/src/composables/useOverviewDashboard.ts
rename to apps/ui/src/render/overview/useOverviewDashboard.ts
diff --git a/apps/ui/src/composables/useOverviewDashboards.ts 
b/apps/ui/src/render/overview/useOverviewDashboards.ts
similarity index 97%
rename from apps/ui/src/composables/useOverviewDashboards.ts
rename to apps/ui/src/render/overview/useOverviewDashboards.ts
index 4fc87fc..e0fe988 100644
--- a/apps/ui/src/composables/useOverviewDashboards.ts
+++ b/apps/ui/src/render/overview/useOverviewDashboards.ts
@@ -18,7 +18,7 @@
 import { computed } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
 import { bffClient } from '@/api/client';
-import { useLayers } from './useLayers';
+import { useLayers } from '../../shell/useLayers';
 
 /**
  * Overview-dashboard list driver. Fetches the BFF's bundled list, then
diff --git a/apps/ui/src/components/overview/widgets/AlarmsWidget.vue 
b/apps/ui/src/render/widgets/AlarmsWidget.vue
similarity index 100%
rename from apps/ui/src/components/overview/widgets/AlarmsWidget.vue
rename to apps/ui/src/render/widgets/AlarmsWidget.vue
diff --git a/apps/ui/src/components/overview/widgets/K8sSummaryWidget.vue 
b/apps/ui/src/render/widgets/K8sSummaryWidget.vue
similarity index 98%
rename from apps/ui/src/components/overview/widgets/K8sSummaryWidget.vue
rename to apps/ui/src/render/widgets/K8sSummaryWidget.vue
index ecdcb21..4e1b65f 100644
--- a/apps/ui/src/components/overview/widgets/K8sSummaryWidget.vue
+++ b/apps/ui/src/render/widgets/K8sSummaryWidget.vue
@@ -25,7 +25,7 @@
 <script setup lang="ts">
 import { computed } from 'vue';
 import { RouterLink } from 'vue-router';
-import { K8S_SUMMARY_KPIS } from '@/composables/useOverviewDashboard';
+import { K8S_SUMMARY_KPIS } from '@/render/overview/useOverviewDashboard';
 import { formatValue } from './ValueFormat';
 
 const props = defineProps<{
diff --git a/apps/ui/src/components/overview/widgets/KpiTileWidget.vue 
b/apps/ui/src/render/widgets/KpiTileWidget.vue
similarity index 100%
rename from apps/ui/src/components/overview/widgets/KpiTileWidget.vue
rename to apps/ui/src/render/widgets/KpiTileWidget.vue
diff --git a/apps/ui/src/components/overview/widgets/MetricWidget.vue 
b/apps/ui/src/render/widgets/MetricWidget.vue
similarity index 100%
rename from apps/ui/src/components/overview/widgets/MetricWidget.vue
rename to apps/ui/src/render/widgets/MetricWidget.vue
diff --git a/apps/ui/src/components/overview/widgets/PilotSummaryWidget.vue 
b/apps/ui/src/render/widgets/PilotSummaryWidget.vue
similarity index 97%
rename from apps/ui/src/components/overview/widgets/PilotSummaryWidget.vue
rename to apps/ui/src/render/widgets/PilotSummaryWidget.vue
index 0ff0c3a..3bb358e 100644
--- a/apps/ui/src/components/overview/widgets/PilotSummaryWidget.vue
+++ b/apps/ui/src/render/widgets/PilotSummaryWidget.vue
@@ -23,7 +23,7 @@
 <script setup lang="ts">
 import { computed } from 'vue';
 import { RouterLink } from 'vue-router';
-import { PILOT_SUMMARY_KPIS } from '@/composables/useOverviewDashboard';
+import { PILOT_SUMMARY_KPIS } from '@/render/overview/useOverviewDashboard';
 import { formatValue } from './ValueFormat';
 
 const props = defineProps<{
diff --git a/apps/ui/src/components/overview/widgets/SectionBreak.vue 
b/apps/ui/src/render/widgets/SectionBreak.vue
similarity index 100%
rename from apps/ui/src/components/overview/widgets/SectionBreak.vue
rename to apps/ui/src/render/widgets/SectionBreak.vue
diff --git a/apps/ui/src/components/overview/widgets/ServiceCountWidget.vue 
b/apps/ui/src/render/widgets/ServiceCountWidget.vue
similarity index 100%
rename from apps/ui/src/components/overview/widgets/ServiceCountWidget.vue
rename to apps/ui/src/render/widgets/ServiceCountWidget.vue
diff --git a/apps/ui/src/components/overview/widgets/ValueFormat.ts 
b/apps/ui/src/render/widgets/ValueFormat.ts
similarity index 100%
rename from apps/ui/src/components/overview/widgets/ValueFormat.ts
rename to apps/ui/src/render/widgets/ValueFormat.ts
diff --git a/apps/ui/src/components/shell/AdminFeatureWarning.vue 
b/apps/ui/src/shell/AdminFeatureWarning.vue
similarity index 99%
rename from apps/ui/src/components/shell/AdminFeatureWarning.vue
rename to apps/ui/src/shell/AdminFeatureWarning.vue
index 977ca7b..d6f089f 100644
--- a/apps/ui/src/components/shell/AdminFeatureWarning.vue
+++ b/apps/ui/src/shell/AdminFeatureWarning.vue
@@ -32,7 +32,7 @@
  * `useAdminFeatures()` state and the `module` prop.
  */
 import { computed } from 'vue';
-import { useAdminFeatures } from '@/composables/useAdminFeatures';
+import { useAdminFeatures } from '@/shell/useAdminFeatures';
 import Icon from '@/components/icons/Icon.vue';
 
 const props = defineProps<{
diff --git a/apps/ui/src/components/shell/AppShell.vue 
b/apps/ui/src/shell/AppShell.vue
similarity index 93%
rename from apps/ui/src/components/shell/AppShell.vue
rename to apps/ui/src/shell/AppShell.vue
index 67551f7..0c78599 100644
--- a/apps/ui/src/components/shell/AppShell.vue
+++ b/apps/ui/src/shell/AppShell.vue
@@ -19,8 +19,8 @@ import { RouterView } from 'vue-router';
 import AppSidebar from './AppSidebar.vue';
 import AppTopbar from './AppTopbar.vue';
 import GlobalConnectivityBanner from './GlobalConnectivityBanner.vue';
-import TracePopout from '@/components/trace/TracePopout.vue';
-import ZipkinTracePopout from '@/components/trace/ZipkinTracePopout.vue';
+import TracePopout from '@/layer/traces/TracePopout.vue';
+import ZipkinTracePopout from '@/layer/traces/ZipkinTracePopout.vue';
 </script>
 
 <template>
diff --git a/apps/ui/src/components/shell/AppSidebar.vue 
b/apps/ui/src/shell/AppSidebar.vue
similarity index 99%
rename from apps/ui/src/components/shell/AppSidebar.vue
rename to apps/ui/src/shell/AppSidebar.vue
index 380bfdc..669fdf4 100644
--- a/apps/ui/src/components/shell/AppSidebar.vue
+++ b/apps/ui/src/shell/AppSidebar.vue
@@ -19,10 +19,10 @@ import { computed, ref, watch } from 'vue';
 import { RouterLink, useRoute, useRouter } from 'vue-router';
 import Icon, { type IconName } from '@/components/icons/Icon.vue';
 import logoSw from '@/assets/icons/logo-sw.svg?raw';
-import { useAuthStore } from '@/stores/auth';
-import { useLayers, firstLayerTab } from '@/composables/useLayers';
-import { useLandingOrder } from '@/composables/useLandingOrder';
-import { useOverviewDashboards } from '@/composables/useOverviewDashboards';
+import { useAuthStore } from '@/state/auth';
+import { useLayers, firstLayerTab } from '@/shell/useLayers';
+import { useLandingOrder } from '@/shell/useLandingOrder';
+import { useOverviewDashboards } from 
'@/render/overview/useOverviewDashboards';
 
 const auth = useAuthStore();
 const router = useRouter();
diff --git a/apps/ui/src/components/shell/AppTopbar.vue 
b/apps/ui/src/shell/AppTopbar.vue
similarity index 99%
rename from apps/ui/src/components/shell/AppTopbar.vue
rename to apps/ui/src/shell/AppTopbar.vue
index 0f9b640..6db97c3 100644
--- a/apps/ui/src/components/shell/AppTopbar.vue
+++ b/apps/ui/src/shell/AppTopbar.vue
@@ -18,10 +18,10 @@
 import { computed, ref, watch } from 'vue';
 import { RouterLink, useRoute } from 'vue-router';
 import Icon from '@/components/icons/Icon.vue';
-import { useOapInfo } from '@/composables/useOapInfo';
-import { useLayers } from '@/composables/useLayers';
-import { useAutoRefreshStore } from '@/stores/autoRefresh';
-import { useTimeRangeStore, TIME_PRESETS, STEP_LIMITS, isValidRange, type 
TimeStep } from '@/stores/timeRange';
+import { useOapInfo } from '@/shell/useOapInfo';
+import { useLayers } from '@/shell/useLayers';
+import { useAutoRefreshStore } from '@/controls/autoRefresh';
+import { useTimeRangeStore, TIME_PRESETS, STEP_LIMITS, isValidRange, type 
TimeStep } from '@/controls/timeRange';
 
 const route = useRoute();
 const { layers } = useLayers();
diff --git a/apps/ui/src/components/shell/GlobalConnectivityBanner.vue 
b/apps/ui/src/shell/GlobalConnectivityBanner.vue
similarity index 99%
rename from apps/ui/src/components/shell/GlobalConnectivityBanner.vue
rename to apps/ui/src/shell/GlobalConnectivityBanner.vue
index 6a6ce8e..b3dae94 100644
--- a/apps/ui/src/components/shell/GlobalConnectivityBanner.vue
+++ b/apps/ui/src/shell/GlobalConnectivityBanner.vue
@@ -34,7 +34,7 @@
  */
 import { computed, onBeforeUnmount, ref, watch } from 'vue';
 import { useRoute } from 'vue-router';
-import { useOapInfo } from '@/composables/useOapInfo';
+import { useOapInfo } from '@/shell/useOapInfo';
 import Icon from '@/components/icons/Icon.vue';
 
 type RetryChoice = 'off' | '5' | '15' | '60';
diff --git a/apps/ui/src/views/landing/LandingView.vue 
b/apps/ui/src/shell/LandingView.vue
similarity index 100%
rename from apps/ui/src/views/landing/LandingView.vue
rename to apps/ui/src/shell/LandingView.vue
diff --git a/apps/ui/src/views/PlaceholderView.vue 
b/apps/ui/src/shell/PlaceholderView.vue
similarity index 100%
rename from apps/ui/src/views/PlaceholderView.vue
rename to apps/ui/src/shell/PlaceholderView.vue
diff --git a/apps/ui/src/router/index.ts b/apps/ui/src/shell/router/index.ts
similarity index 73%
rename from apps/ui/src/router/index.ts
rename to apps/ui/src/shell/router/index.ts
index 503e1c2..7a36580 100644
--- a/apps/ui/src/router/index.ts
+++ b/apps/ui/src/shell/router/index.ts
@@ -15,9 +15,9 @@
  * limitations under the License.
  */
 import { createRouter, createWebHistory, type RouteRecordRaw } from 
'vue-router';
-import { useAuthStore } from '@/stores/auth';
+import { useAuthStore } from '@/state/auth';
 
-const placeholder = () => import('@/views/PlaceholderView.vue');
+const placeholder = () => import('@/shell/PlaceholderView.vue');
 
 function humanKey(k: string): string {
   return k.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
@@ -38,7 +38,7 @@ function layerRoute(): RouteRecordRaw {
   const placeholderTabs: { path: string; label: string; phase: string }[] = [];
   return {
     path: 'layer/:layerKey',
-    component: () => import('@/views/layer/LayerShell.vue'),
+    component: () => import('@/layer/LayerShell.vue'),
     children: [
       // Bare /layer/:layerKey lands on the Service view — the per-layer
       // widget grid driven by the dashboard config.
@@ -46,19 +46,19 @@ function layerRoute(): RouteRecordRaw {
       // Per-scope dashboards. Same view component, scope inferred from
       // the URL — widget set differs per scope via the JSON template's
       // `dashboards.<scope>` array.
-      { path: 'service', component: () => 
import('@/views/layer/LayerDashboardsView.vue') },
-      { path: 'instance', component: () => 
import('@/views/layer/LayerDashboardsView.vue') },
-      { path: 'endpoint', component: () => 
import('@/views/layer/LayerDashboardsView.vue') },
+      { path: 'service', component: () => 
import('@/render/layer-dashboard/LayerDashboardsView.vue') },
+      { path: 'instance', component: () => 
import('@/render/layer-dashboard/LayerDashboardsView.vue') },
+      { path: 'endpoint', component: () => 
import('@/render/layer-dashboard/LayerDashboardsView.vue') },
       {
         path: 'topology',
-        component: () => import('@/views/layer/LayerServiceMapView.vue'),
+        component: () => import('@/layer/service-map/LayerServiceMapView.vue'),
         // The topology page ships its own in-box service-focus selector
         // (the map is layer-wide by default). Declaring it here keeps
         // the LayerShell's header picker hidden for this route — no
         // route-string sniffing in the shell.
         meta: { ownsServiceSelector: true },
       },
-      { path: 'dependency', component: () => 
import('@/views/layer/LayerEndpointDependencyView.vue') },
+      { path: 'dependency', component: () => 
import('@/layer/endpoint-dependency/LayerEndpointDependencyView.vue') },
       // `LayerTracesEntry` is a runtime dispatcher: it inspects the
       // layer template's `traces.source` and renders either the native
       // trace view or the Zipkin one. Mesh / k8s layers land on Zipkin.
@@ -67,13 +67,13 @@ function layerRoute(): RouteRecordRaw {
       // own service input, and Zipkin's service universe is decoupled
       // from SkyWalking's anyway (different name index, no `normal`
       // flag, queried via `/api/v2/services`).
-      { path: 'trace', component: () => 
import('@/views/layer/LayerTracesEntry.vue'), meta: { ownsServiceSelector: true 
} },
-      { path: 'logs', component: () => 
import('@/views/layer/LayerLogsView.vue') },
-      { path: 'trace-profiling', component: () => 
import('@/views/layer/LayerTraceProfilingView.vue') },
-      { path: 'ebpf-profiling', component: () => 
import('@/views/layer/LayerEBPFProfilingView.vue') },
-      { path: 'async-profiling', component: () => 
import('@/views/layer/LayerAsyncProfilingView.vue') },
-      { path: 'network-profiling', component: () => 
import('@/views/layer/LayerNetworkProfilingView.vue') },
-      { path: 'pprof', component: () => 
import('@/views/layer/LayerPprofProfilingView.vue') },
+      { path: 'trace', component: () => 
import('@/layer/traces/LayerTracesEntry.vue'), meta: { ownsServiceSelector: 
true } },
+      { path: 'logs', component: () => 
import('@/layer/logs/LayerLogsView.vue') },
+      { path: 'trace-profiling', component: () => 
import('@/layer/profiling/LayerTraceProfilingView.vue') },
+      { path: 'ebpf-profiling', component: () => 
import('@/layer/profiling/LayerEBPFProfilingView.vue') },
+      { path: 'async-profiling', component: () => 
import('@/layer/profiling/LayerAsyncProfilingView.vue') },
+      { path: 'network-profiling', component: () => 
import('@/layer/profiling/LayerNetworkProfilingView.vue') },
+      { path: 'pprof', component: () => 
import('@/layer/profiling/LayerPprofProfilingView.vue') },
       // Old single-profiling URL → redirect to the trace-profiling page
       // for back-compat with bookmarks taken before the split.
       {
@@ -110,7 +110,7 @@ function layerRoute(): RouteRecordRaw {
       },
       ...placeholderTabs.map<RouteRecordRaw>((f) => ({
         path: f.path,
-        component: () => import('@/views/layer/LayerTabPlaceholder.vue'),
+        component: () => import('@/layer/LayerTabPlaceholder.vue'),
         props: (r) => ({
           title: `${humanKey(String(r.params.layerKey))} · ${f.label}`,
           phase: f.phase,
@@ -121,20 +121,20 @@ function layerRoute(): RouteRecordRaw {
 }
 
 const shellRoutes: RouteRecordRaw[] = [
-  { path: '', name: 'overview', component: () => 
import('@/views/overview/OverviewLanding.vue') },
+  { path: '', name: 'overview', component: () => 
import('@/render/overview/OverviewLanding.vue') },
   {
     path: 'overview/:id',
     name: 'overview-dashboard',
-    component: () => import('@/views/overview/OverviewDashboardView.vue'),
+    component: () => import('@/render/overview/OverviewDashboardView.vue'),
   },
-  { path: 'setup', name: 'setup', component: () => 
import('@/views/setup/SetupView.vue') },
+  { path: 'setup', name: 'setup', component: () => 
import('@/features/setup/SetupView.vue') },
   layerRoute(),
   // Alarms — independent page (not a layer template / overview).
   // OAP `getAlarm` proxy + background-traffic timeline + per-layer
   // grouping. Read-only; OAP auto-recovers, no acknowledge / silence.
-  { path: 'alarms', name: 'alarms', component: () => 
import('@/views/alarms/AlarmsView.vue') },
+  { path: 'alarms', name: 'alarms', component: () => 
import('@/features/alarms/AlarmsView.vue') },
   // Cluster
-  { path: 'operate/cluster', component: () => 
import('@/views/operate/ClusterStatusView.vue') },
+  { path: 'operate/cluster', component: () => 
import('@/features/operate/cluster/ClusterStatusView.vue') },
   // ── DSL Management ─────────────────────────────────────────────────
   // Static sub-routes are declared first so they aren't shadowed by
   // the catalog alternation regex (which would otherwise grab `edit`
@@ -143,23 +143,23 @@ const shellRoutes: RouteRecordRaw[] = [
   {
     path: 'operate/dsl/edit',
     name: 'edit',
-    component: () => import('@/views/operate/dsl/DslEditorView.vue'),
+    component: () => import('@/features/operate/dsl/DslEditorView.vue'),
   },
   {
     path: 'operate/dsl/dump',
     name: 'dump',
-    component: () => import('@/views/operate/dsl/DslDumpView.vue'),
+    component: () => import('@/features/operate/dsl/DslDumpView.vue'),
   },
   {
     path: 'operate/dsl/:catalog(otel-rules|telegraf-rules|lal|log-mal-rules)',
     name: 'catalog',
-    component: () => import('@/views/operate/dsl/DslCatalogView.vue'),
+    component: () => import('@/features/operate/dsl/DslCatalogView.vue'),
     props: true,
   },
   {
     path: 'operate/oal',
     name: 'oal-catalog',
-    component: () => import('@/views/operate/dsl/OalCatalogView.vue'),
+    component: () => import('@/features/operate/dsl/OalCatalogView.vue'),
   },
   // Inspect — gated on the `inspect` module (and `receiver-runtime-rule`
   // for rule attribution; degrades cleanly to "unknown" attribution
@@ -167,7 +167,7 @@ const shellRoutes: RouteRecordRaw[] = [
   {
     path: 'operate/inspect',
     name: 'inspect',
-    component: () => import('@/views/operate/InspectView.vue'),
+    component: () => import('@/features/operate/inspect/InspectView.vue'),
   },
   // Live debugger — gated on `dsl-debugging`. History is local-only
   // (browser localStorage) so it stays useful even when admin is down.
@@ -175,24 +175,24 @@ const shellRoutes: RouteRecordRaw[] = [
   {
     path: 'operate/live-debug/history',
     name: 'debug-history',
-    component: () => import('@/views/operate/live-debug/DebugHistoryView.vue'),
+    component: () => 
import('@/features/operate/live-debug/DebugHistoryView.vue'),
   },
   {
     path: 'operate/live-debug/:tab(mal|lal|oal)?',
     name: 'live-debugger',
-    component: () => import('@/views/operate/live-debug/LiveDebuggerView.vue'),
+    component: () => 
import('@/features/operate/live-debug/LiveDebuggerView.vue'),
   },
   // Admin
   {
     path: 'admin/layer-dashboards',
-    component: () => import('@/views/admin/LayerDashboardsAdmin.vue'),
+    component: () => 
import('@/features/admin/layer-templates/LayerDashboardsAdmin.vue'),
   },
   // Alert page setup — sits under Dashboard setup in the sidebar but
   // routes off the admin tree since it's an operator-only config view.
   {
     path: 'admin/alert-page-setup',
     name: 'alert-page-setup',
-    component: () => import('@/views/admin/AlertPageSetupView.vue'),
+    component: () => 
import('@/features/admin/alert-page/AlertPageSetupView.vue'),
   },
   { path: 'admin/users', component: placeholder, props: { title: 'Users', 
phase: 'Phase 7' } },
   { path: 'admin/roles', component: placeholder, props: { title: 'Roles & 
permissions', phase: 'Phase 7' } },
@@ -204,12 +204,12 @@ const router = createRouter({
     {
       path: '/login',
       name: 'login',
-      component: () => import('@/views/auth/LoginView.vue'),
+      component: () => import('@/features/auth/LoginView.vue'),
       meta: { public: true },
     },
     {
       path: '/',
-      component: () => import('@/components/shell/AppShell.vue'),
+      component: () => import('@/shell/AppShell.vue'),
       children: shellRoutes,
     },
     {
diff --git a/apps/ui/src/composables/useAdminFeatures.ts 
b/apps/ui/src/shell/useAdminFeatures.ts
similarity index 97%
rename from apps/ui/src/composables/useAdminFeatures.ts
rename to apps/ui/src/shell/useAdminFeatures.ts
index 7550b5d..8034a67 100644
--- a/apps/ui/src/composables/useAdminFeatures.ts
+++ b/apps/ui/src/shell/useAdminFeatures.ts
@@ -19,7 +19,7 @@ import { computed } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
 import type { PreflightResult } from '@skywalking-horizon-ui/api-client';
 import { bffClient } from '@/api/client';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from '../controls/useAutoRefreshSubscribe';
 
 /**
  * Admin-port preflight — interrogates OAP's `/debugging/config/dump`
diff --git a/apps/ui/src/composables/useLandingOrder.ts 
b/apps/ui/src/shell/useLandingOrder.ts
similarity index 97%
rename from apps/ui/src/composables/useLandingOrder.ts
rename to apps/ui/src/shell/useLandingOrder.ts
index 5a1fafe..d1994be 100644
--- a/apps/ui/src/composables/useLandingOrder.ts
+++ b/apps/ui/src/shell/useLandingOrder.ts
@@ -17,7 +17,7 @@
 
 import { computed, type ComputedRef } from 'vue';
 import type { LayerDef } from '@skywalking-horizon-ui/api-client';
-import { useSetupStore } from '@/stores/setup';
+import { useSetupStore } from '@/state/setup';
 
 /**
  * Sort layers by `landing.priority` (lower first). Ties break by the OAP
diff --git a/apps/ui/src/composables/useLayers.ts 
b/apps/ui/src/shell/useLayers.ts
similarity index 100%
rename from apps/ui/src/composables/useLayers.ts
rename to apps/ui/src/shell/useLayers.ts
diff --git a/apps/ui/src/composables/useOapInfo.ts 
b/apps/ui/src/shell/useOapInfo.ts
similarity index 98%
rename from apps/ui/src/composables/useOapInfo.ts
rename to apps/ui/src/shell/useOapInfo.ts
index a233c54..32690f6 100644
--- a/apps/ui/src/composables/useOapInfo.ts
+++ b/apps/ui/src/shell/useOapInfo.ts
@@ -17,7 +17,7 @@
 
 import { computed } from 'vue';
 import { useQuery } from '@tanstack/vue-query';
-import { useAutoRefreshSubscribe } from './useAutoRefreshSubscribe';
+import { useAutoRefreshSubscribe } from '../controls/useAutoRefreshSubscribe';
 import { parseOapTimezoneMinutes, type OapInfo } from 
'@skywalking-horizon-ui/api-client';
 import { bffClient } from '@/api/client';
 
diff --git a/apps/ui/src/stores/auth.ts b/apps/ui/src/state/auth.ts
similarity index 100%
rename from apps/ui/src/stores/auth.ts
rename to apps/ui/src/state/auth.ts
diff --git a/apps/ui/src/stores/setup.ts b/apps/ui/src/state/setup.ts
similarity index 99%
rename from apps/ui/src/stores/setup.ts
rename to apps/ui/src/state/setup.ts
index e0b1183..a37313c 100644
--- a/apps/ui/src/stores/setup.ts
+++ b/apps/ui/src/state/setup.ts
@@ -30,7 +30,7 @@ import { bffClient } from '@/api/client';
 import {
   defaultColumnsForLayer,
   defaultOrderByForLayer,
-} from '@/composables/metricCatalog';
+} from '@/utils/metricCatalog';
 
 export type { LayerConfig, LandingConfig };
 
diff --git a/apps/ui/src/composables/metricCatalog.ts 
b/apps/ui/src/utils/metricCatalog.ts
similarity index 100%
rename from apps/ui/src/composables/metricCatalog.ts
rename to apps/ui/src/utils/metricCatalog.ts
diff --git a/apps/ui/src/composables/metricColor.ts 
b/apps/ui/src/utils/metricColor.ts
similarity index 100%
rename from apps/ui/src/composables/metricColor.ts
rename to apps/ui/src/utils/metricColor.ts


Reply via email to