selectByDriverId(Long driverId);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/DriverFacade.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/DriverFacade.java
new file mode 100644
index 000000000..9d318054c
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/DriverFacade.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.api;
+
+import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
+
+/**
+ * Protocol-neutral driver facade.
+ *
+ * Mirrors the three RPCs on {@code api.center.manager.DriverApi}.
+ * Implementation selection follows the same {@code dc3.facade.mode} switch
+ * used by {@link DeviceFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+public interface DriverFacade {
+
+ /**
+ * @return the driver, or {@code null} when it does not exist.
+ */
+ FacadeDriverBO selectById(Long id);
+
+ /**
+ * @return a page of drivers (never {@code null}; empty page when nothing matches).
+ */
+ FacadePage selectByPage(FacadeDriverQuery query);
+
+ /**
+ * Resolve the driver that owns a given device.
+ *
+ * @return the driver, or {@code null} when the device has no bound driver.
+ */
+ FacadeDriverBO selectByDeviceId(Long deviceId);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/PointFacade.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/PointFacade.java
new file mode 100644
index 000000000..d644d1a2b
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/PointFacade.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.api;
+
+import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
+
+/**
+ * Protocol-neutral point facade. Mirrors the two RPCs on
+ * {@code api.center.manager.PointApi}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+public interface PointFacade {
+
+ /**
+ * @return the point, or {@code null} when it does not exist.
+ */
+ FacadePointBO selectById(Long id);
+
+ /**
+ * @return a page of points (never {@code null}; empty page when nothing matches).
+ */
+ FacadePage selectByPage(FacadePointQuery query);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/TenantFacade.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/TenantFacade.java
new file mode 100644
index 000000000..05494396b
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/TenantFacade.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.api;
+
+import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
+
+/**
+ * Protocol-neutral tenant facade. Mirrors {@code api.center.auth.TenantApi}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+public interface TenantFacade {
+
+ /**
+ * @return the tenant, or {@code null} when no tenant carries the given code.
+ */
+ FacadeTenantBO selectByCode(String code);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/TokenFacade.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/TokenFacade.java
new file mode 100644
index 000000000..24a854b1e
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/TokenFacade.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.api;
+
+/**
+ * Protocol-neutral token facade. Mirrors {@code api.center.auth.TokenApi}.
+ *
+ * Returns a plain boolean rather than a {@code TokenBO} because callers only
+ * care whether the token is valid — the existing RPC only returns an expiry
+ * string that nobody reads from the gateway.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+public interface TokenFacade {
+
+ /**
+ * Validate a token triple against the backing auth service.
+ *
+ * @param tenant tenant code
+ * @param name login name
+ * @param salt salt the client holds
+ * @param token token the client holds
+ * @return {@code true} when the triple is valid and unexpired
+ */
+ boolean checkValid(String tenant, String name, String salt, String token);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/UserFacade.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/UserFacade.java
new file mode 100644
index 000000000..6a87c2636
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/UserFacade.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.api;
+
+import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
+
+/**
+ * Protocol-neutral user facade. Mirrors {@code api.center.auth.UserApi}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+public interface UserFacade {
+
+ /**
+ * @return the user, or {@code null} when the id does not match any user.
+ */
+ FacadeUserBO selectById(Long id);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/UserLoginFacade.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/UserLoginFacade.java
new file mode 100644
index 000000000..8e42aa01f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/api/UserLoginFacade.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.api;
+
+import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
+
+/**
+ * Protocol-neutral user-login facade. Mirrors {@code api.center.auth.UserLoginApi}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+public interface UserLoginFacade {
+
+ /**
+ * @return the user-login record, or {@code null} when the login name does not exist.
+ */
+ FacadeUserLoginBO selectByName(String name);
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeDeviceBO.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeDeviceBO.java
new file mode 100644
index 000000000..d6a6bbf6b
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeDeviceBO.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.bo;
+
+import io.github.pnoker.common.entity.base.BaseBO;
+import io.github.pnoker.common.entity.ext.DeviceExt;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * Facade-level device BO.
+ *
+ * Field set matches {@code api.center.manager.DeviceApi} wire shape. Intentionally
+ * excludes driver-side attachments (pointIds, attributeConfig maps) — those remain
+ * in {@code dc3-common-driver}'s DeviceBO and are not part of the center-to-center
+ * contract.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+public class FacadeDeviceBO extends BaseBO {
+
+ private String deviceName;
+
+ private String deviceCode;
+
+ private Long driverId;
+
+ private DeviceExt deviceExt;
+
+ private EnableFlagEnum enableFlag;
+
+ private Long tenantId;
+
+ private String signature;
+
+ private Integer version;
+
+ private List profileIds;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeDriverBO.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeDriverBO.java
new file mode 100644
index 000000000..c198e0dfd
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeDriverBO.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.bo;
+
+import io.github.pnoker.common.entity.base.BaseBO;
+import io.github.pnoker.common.entity.ext.DriverExt;
+import io.github.pnoker.common.enums.DriverTypeFlagEnum;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import lombok.*;
+
+/**
+ * Facade-level driver BO. Field set matches {@code api.center.manager.DriverApi}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+public class FacadeDriverBO extends BaseBO {
+
+ private String driverName;
+
+ private String driverCode;
+
+ private String serviceName;
+
+ private String serviceHost;
+
+ private DriverTypeFlagEnum driverTypeFlag;
+
+ private DriverExt driverExt;
+
+ private EnableFlagEnum enableFlag;
+
+ private Long tenantId;
+
+ private String signature;
+
+ private Integer version;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadePointBO.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadePointBO.java
new file mode 100644
index 000000000..b718d4a97
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadePointBO.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.bo;
+
+import io.github.pnoker.common.entity.base.BaseBO;
+import io.github.pnoker.common.entity.ext.PointExt;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.enums.PointTypeFlagEnum;
+import io.github.pnoker.common.enums.RwFlagEnum;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * Facade-level point BO. Field set matches {@code api.center.manager.PointApi}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+public class FacadePointBO extends BaseBO {
+
+ private String pointName;
+
+ private String pointCode;
+
+ private PointTypeFlagEnum pointTypeFlag;
+
+ private RwFlagEnum rwFlag;
+
+ private BigDecimal baseValue;
+
+ private BigDecimal multiple;
+
+ private Byte valueDecimal;
+
+ private String unit;
+
+ private Long profileId;
+
+ private PointExt pointExt;
+
+ private EnableFlagEnum enableFlag;
+
+ private Long tenantId;
+
+ private String signature;
+
+ private Integer version;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeTenantBO.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeTenantBO.java
new file mode 100644
index 000000000..7937a4102
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeTenantBO.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.bo;
+
+import io.github.pnoker.common.entity.base.BaseBO;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import lombok.*;
+
+/**
+ * Facade-level tenant BO. Field set matches {@code api.center.auth.TenantApi}
+ * wire shape (no tenantExt — that is not exposed through gRPC).
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+public class FacadeTenantBO extends BaseBO {
+
+ private String tenantName;
+
+ private String tenantCode;
+
+ private EnableFlagEnum enableFlag;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeUserBO.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeUserBO.java
new file mode 100644
index 000000000..a5537552a
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeUserBO.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.bo;
+
+import io.github.pnoker.common.entity.base.BaseBO;
+import lombok.*;
+
+/**
+ * Facade-level user BO. Field set matches {@code api.center.auth.UserApi} wire
+ * shape — {@code enableFlag} is intentionally absent because the
+ * {@code GrpcUserDTO} contract does not expose it.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+public class FacadeUserBO extends BaseBO {
+
+ private String nickName;
+
+ private String userName;
+
+ private String phone;
+
+ private String email;
+
+ private String socialExt;
+
+ private String identityExt;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeUserLoginBO.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeUserLoginBO.java
new file mode 100644
index 000000000..ed11f5133
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/bo/FacadeUserLoginBO.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.bo;
+
+import io.github.pnoker.common.entity.base.BaseBO;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import lombok.*;
+
+/**
+ * Facade-level user-login BO. Field set matches
+ * {@code api.center.auth.UserLoginApi} wire shape.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+public class FacadeUserLoginBO extends BaseBO {
+
+ private String loginName;
+
+ private Long userId;
+
+ private Long userPasswordId;
+
+ private EnableFlagEnum enableFlag;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/common/FacadePage.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/common/FacadePage.java
new file mode 100644
index 000000000..177833505
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/common/FacadePage.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.common;
+
+import lombok.*;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Transport-neutral page envelope for facade return values.
+ *
+ * Intentionally does not extend MyBatis-Plus {@code Page} so that
+ * {@code dc3-common-facade-api} keeps zero persistence dependencies.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+public class FacadePage implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private long current;
+
+ private long size;
+
+ private long total;
+
+ private long pages;
+
+ private List records;
+
+ public static FacadePage empty() {
+ return new FacadePage<>(1L, 0L, 0L, 0L, Collections.emptyList());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadeDeviceQuery.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadeDeviceQuery.java
new file mode 100644
index 000000000..e835517f2
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadeDeviceQuery.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.query;
+
+import io.github.pnoker.common.entity.common.Pages;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import lombok.*;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Facade-level device query.
+ *
+ * Field set matches the filters supported by {@code DeviceApi.SelectByPage}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@Builder
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+public class FacadeDeviceQuery implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Pages page;
+
+ private Long tenantId;
+
+ private String deviceName;
+
+ private String deviceCode;
+
+ private Long driverId;
+
+ private EnableFlagEnum enableFlag;
+
+ private Integer version;
+
+ private Long profileId;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadeDriverQuery.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadeDriverQuery.java
new file mode 100644
index 000000000..4e0965c78
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadeDriverQuery.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.query;
+
+import io.github.pnoker.common.entity.common.Pages;
+import io.github.pnoker.common.enums.DriverTypeFlagEnum;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import lombok.*;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Facade-level driver query.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@Builder
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+public class FacadeDriverQuery implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Pages page;
+
+ private Long tenantId;
+
+ private String driverName;
+
+ private String driverCode;
+
+ private String serviceName;
+
+ private String serviceHost;
+
+ private DriverTypeFlagEnum driverTypeFlag;
+
+ private EnableFlagEnum enableFlag;
+
+ private Integer version;
+}
diff --git a/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadePointQuery.java b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadePointQuery.java
new file mode 100644
index 000000000..321009add
--- /dev/null
+++ b/dc3-common/dc3-common-facade-api/src/main/java/io/github/pnoker/common/facade/entity/query/FacadePointQuery.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.entity.query;
+
+import io.github.pnoker.common.entity.common.Pages;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.enums.PointTypeFlagEnum;
+import io.github.pnoker.common.enums.RwFlagEnum;
+import lombok.*;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Facade-level point query.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Getter
+@Setter
+@Builder
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+public class FacadePointQuery implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Pages page;
+
+ private Long tenantId;
+
+ private String pointName;
+
+ private String pointCode;
+
+ private PointTypeFlagEnum pointTypeFlag;
+
+ private RwFlagEnum rwFlag;
+
+ private Long profileId;
+
+ private EnableFlagEnum enableFlag;
+
+ private Integer version;
+
+ /**
+ * Device ID — filter points bound to a specific device.
+ */
+ private Long deviceId;
+}
diff --git a/dc3-common/dc3-common-facade-grpc/pom.xml b/dc3-common/dc3-common-facade-grpc/pom.xml
new file mode 100644
index 000000000..36e706a9d
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/pom.xml
@@ -0,0 +1,78 @@
+
+
+
+ 4.0.0
+
+
+ io.github.pnoker
+ dc3-common
+ 2026.4.19
+
+
+ ${project.artifactId}
+ dc3-common-facade-grpc
+ 2026.4.19
+ jar
+
+ 2016
+ https://github.com/pnoker/iot-dc3
+
+ gRPC implementation of the Facade API, selected when dc3.facade.mode=grpc (default).
+ Holds BlockingStubs and translates between Facade BO/Query and protobuf DTOs.
+
+
+
+
+
+
+ net.devh
+ grpc-spring-boot-starter
+
+
+
+
+ io.github.pnoker
+ dc3-api-auth
+
+
+ io.github.pnoker
+ dc3-api-manager
+
+
+
+
+ io.github.pnoker
+ dc3-common-api
+
+
+ io.github.pnoker
+ dc3-common-exception
+
+
+ io.github.pnoker
+ dc3-common-facade-api
+
+
+ io.github.pnoker
+ dc3-common-public
+
+
+
+
+
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/DeviceGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/DeviceGrpcFacade.java
new file mode 100644
index 000000000..35b1aea6f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/DeviceGrpcFacade.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.manager.*;
+import io.github.pnoker.api.common.GrpcDeviceDTO;
+import io.github.pnoker.api.common.GrpcR;
+import io.github.pnoker.common.constant.service.ManagerConstant;
+import io.github.pnoker.common.enums.ResponseEnum;
+import io.github.pnoker.common.exception.ServiceException;
+import io.github.pnoker.common.facade.api.DeviceFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
+import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcDeviceBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * gRPC implementation: forwards each call to Manager Center via
+ * {@link DeviceApiGrpc.DeviceApiBlockingStub}.
+ *
+ * Selected when {@code dc3.facade.mode=grpc} (or unset — grpc is the default
+ * in the auto-configuration declaration).
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class DeviceGrpcFacade implements DeviceFacade {
+
+ @GrpcClient(ManagerConstant.SERVICE_NAME)
+ private DeviceApiGrpc.DeviceApiBlockingStub deviceApiBlockingStub;
+
+ @Resource
+ private FacadeGrpcDeviceBuilder facadeGrpcDeviceBuilder;
+
+ @Override
+ public FacadeDeviceBO selectById(Long id) {
+ GrpcDeviceQuery request = GrpcDeviceQuery.newBuilder().setDeviceId(id).build();
+ GrpcRDeviceDTO response = deviceApiBlockingStub.selectByDeviceId(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectById");
+ return null;
+ }
+ return facadeGrpcDeviceBuilder.toFacadeBO(response.getData());
+ }
+
+ @Override
+ public FacadePage selectByPage(FacadeDeviceQuery query) {
+ GrpcPageDeviceQuery request = facadeGrpcDeviceBuilder.toGrpcPageQuery(query);
+ GrpcRPageDeviceDTO response = deviceApiBlockingStub.selectByPage(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByPage");
+ return FacadePage.empty();
+ }
+
+ GrpcPageDeviceDTO pageDTO = response.getData();
+ List records = pageDTO.getDataList().stream()
+ .map(facadeGrpcDeviceBuilder::toFacadeBO)
+ .toList();
+
+ return new FacadePage<>(
+ pageDTO.getPage().getCurrent(),
+ pageDTO.getPage().getSize(),
+ pageDTO.getPage().getTotal(),
+ pageDTO.getPage().getPages(),
+ records);
+ }
+
+ @Override
+ public List selectByProfileId(Long profileId) {
+ GrpcProfileQuery request = GrpcProfileQuery.newBuilder().setProfileId(profileId).build();
+ GrpcRDeviceListDTO response = deviceApiBlockingStub.selectByProfileId(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByProfileId");
+ return Collections.emptyList();
+ }
+ return response.getDataList().stream().map(facadeGrpcDeviceBuilder::toFacadeBO).toList();
+ }
+
+ @Override
+ public List selectByDriverId(Long driverId) {
+ GrpcDriverQuery request = GrpcDriverQuery.newBuilder().setDriverId(driverId).build();
+ GrpcRDeviceListDTO response = deviceApiBlockingStub.selectByDriverId(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByDriverId");
+ return Collections.emptyList();
+ }
+ return response.getDataList().stream().map(facadeGrpcDeviceBuilder::toFacadeBO).toList();
+ }
+
+ /**
+ * NO_RESOURCE is a normal "not found" signal — swallow and let the caller see null / empty.
+ * Any other non-OK code (server error, param error, etc.) escalates to an exception.
+ */
+ private void guardOrThrow(GrpcR result, String op) {
+ String code = result.getCode();
+ if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
+ log.debug("DeviceGrpcFacade.{} => no resource", op);
+ return;
+ }
+ throw new ServiceException("DeviceFacade." + op + " failed: [" + code + "] " + result.getMessage());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/DriverGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/DriverGrpcFacade.java
new file mode 100644
index 000000000..82a67854f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/DriverGrpcFacade.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.manager.*;
+import io.github.pnoker.api.common.GrpcR;
+import io.github.pnoker.common.constant.service.ManagerConstant;
+import io.github.pnoker.common.enums.ResponseEnum;
+import io.github.pnoker.common.exception.ServiceException;
+import io.github.pnoker.common.facade.api.DriverFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
+import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcDriverBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * gRPC DriverFacade: forwards to Manager Center via
+ * {@link DriverApiGrpc.DriverApiBlockingStub}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class DriverGrpcFacade implements DriverFacade {
+
+ @GrpcClient(ManagerConstant.SERVICE_NAME)
+ private DriverApiGrpc.DriverApiBlockingStub driverApiBlockingStub;
+
+ @Resource
+ private FacadeGrpcDriverBuilder facadeGrpcDriverBuilder;
+
+ @Override
+ public FacadeDriverBO selectById(Long id) {
+ GrpcDriverQuery request = GrpcDriverQuery.newBuilder().setDriverId(id).build();
+ GrpcRDriverDTO response = driverApiBlockingStub.selectByDriverId(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectById");
+ return null;
+ }
+ return facadeGrpcDriverBuilder.toFacadeBO(response.getData());
+ }
+
+ @Override
+ public FacadePage selectByPage(FacadeDriverQuery query) {
+ GrpcPageDriverQuery request = facadeGrpcDriverBuilder.toGrpcPageQuery(query);
+ GrpcRPageDriverDTO response = driverApiBlockingStub.selectByPage(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByPage");
+ return FacadePage.empty();
+ }
+
+ GrpcPageDriverDTO pageDTO = response.getData();
+ List records = pageDTO.getDataList().stream()
+ .map(facadeGrpcDriverBuilder::toFacadeBO)
+ .toList();
+
+ return new FacadePage<>(
+ pageDTO.getPage().getCurrent(),
+ pageDTO.getPage().getSize(),
+ pageDTO.getPage().getTotal(),
+ pageDTO.getPage().getPages(),
+ records);
+ }
+
+ @Override
+ public FacadeDriverBO selectByDeviceId(Long deviceId) {
+ GrpcDeviceQuery request = GrpcDeviceQuery.newBuilder().setDeviceId(deviceId).build();
+ GrpcRDriverDTO response = driverApiBlockingStub.selectByDeviceId(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByDeviceId");
+ return null;
+ }
+ return facadeGrpcDriverBuilder.toFacadeBO(response.getData());
+ }
+
+ private void guardOrThrow(GrpcR result, String op) {
+ String code = result.getCode();
+ if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
+ log.debug("DriverGrpcFacade.{} => no resource", op);
+ return;
+ }
+ throw new ServiceException("DriverFacade." + op + " failed: [" + code + "] " + result.getMessage());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/PointGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/PointGrpcFacade.java
new file mode 100644
index 000000000..91bc31413
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/PointGrpcFacade.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.manager.*;
+import io.github.pnoker.api.common.GrpcR;
+import io.github.pnoker.common.constant.service.ManagerConstant;
+import io.github.pnoker.common.enums.ResponseEnum;
+import io.github.pnoker.common.exception.ServiceException;
+import io.github.pnoker.common.facade.api.PointFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
+import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcPointBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * gRPC PointFacade: forwards to Manager Center via
+ * {@link PointApiGrpc.PointApiBlockingStub}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class PointGrpcFacade implements PointFacade {
+
+ @GrpcClient(ManagerConstant.SERVICE_NAME)
+ private PointApiGrpc.PointApiBlockingStub pointApiBlockingStub;
+
+ @Resource
+ private FacadeGrpcPointBuilder facadeGrpcPointBuilder;
+
+ @Override
+ public FacadePointBO selectById(Long id) {
+ GrpcPointQuery request = GrpcPointQuery.newBuilder().setPointId(id).build();
+ GrpcRPointDTO response = pointApiBlockingStub.selectById(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectById");
+ return null;
+ }
+ return facadeGrpcPointBuilder.toFacadeBO(response.getData());
+ }
+
+ @Override
+ public FacadePage selectByPage(FacadePointQuery query) {
+ GrpcPagePointQuery request = facadeGrpcPointBuilder.toGrpcPageQuery(query);
+ GrpcRPagePointDTO response = pointApiBlockingStub.selectByPage(request);
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByPage");
+ return FacadePage.empty();
+ }
+
+ GrpcPagePointDTO pageDTO = response.getData();
+ List records = pageDTO.getDataList().stream()
+ .map(facadeGrpcPointBuilder::toFacadeBO)
+ .toList();
+
+ return new FacadePage<>(
+ pageDTO.getPage().getCurrent(),
+ pageDTO.getPage().getSize(),
+ pageDTO.getPage().getTotal(),
+ pageDTO.getPage().getPages(),
+ records);
+ }
+
+ private void guardOrThrow(GrpcR result, String op) {
+ String code = result.getCode();
+ if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
+ log.debug("PointGrpcFacade.{} => no resource", op);
+ return;
+ }
+ throw new ServiceException("PointFacade." + op + " failed: [" + code + "] " + result.getMessage());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/TenantGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/TenantGrpcFacade.java
new file mode 100644
index 000000000..7ff20ba73
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/TenantGrpcFacade.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.auth.GrpcCodeQuery;
+import io.github.pnoker.api.center.auth.GrpcRTenantDTO;
+import io.github.pnoker.api.center.auth.TenantApiGrpc;
+import io.github.pnoker.api.common.GrpcR;
+import io.github.pnoker.common.constant.service.AuthConstant;
+import io.github.pnoker.common.enums.ResponseEnum;
+import io.github.pnoker.common.exception.ServiceException;
+import io.github.pnoker.common.facade.api.TenantFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
+import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcTenantBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * gRPC {@link TenantFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class TenantGrpcFacade implements TenantFacade {
+
+ @GrpcClient(AuthConstant.SERVICE_NAME)
+ private TenantApiGrpc.TenantApiBlockingStub tenantApiBlockingStub;
+
+ @Resource
+ private FacadeGrpcTenantBuilder facadeGrpcTenantBuilder;
+
+ @Override
+ public FacadeTenantBO selectByCode(String code) {
+ GrpcRTenantDTO response = tenantApiBlockingStub.selectByCode(GrpcCodeQuery.newBuilder().setCode(code).build());
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByCode");
+ return null;
+ }
+ return facadeGrpcTenantBuilder.toFacadeBO(response.getData());
+ }
+
+ private void guardOrThrow(GrpcR result, String op) {
+ String code = result.getCode();
+ if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
+ log.debug("TenantGrpcFacade.{} => no resource", op);
+ return;
+ }
+ throw new ServiceException("TenantFacade." + op + " failed: [" + code + "] " + result.getMessage());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/TokenGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/TokenGrpcFacade.java
new file mode 100644
index 000000000..c51112c4d
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/TokenGrpcFacade.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.auth.GrpcLoginQuery;
+import io.github.pnoker.api.center.auth.GrpcRTokenDTO;
+import io.github.pnoker.api.center.auth.TokenApiGrpc;
+import io.github.pnoker.common.constant.service.AuthConstant;
+import io.github.pnoker.common.facade.api.TokenFacade;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * gRPC {@link TokenFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class TokenGrpcFacade implements TokenFacade {
+
+ @GrpcClient(AuthConstant.SERVICE_NAME)
+ private TokenApiGrpc.TokenApiBlockingStub tokenApiBlockingStub;
+
+ @Override
+ public boolean checkValid(String tenant, String name, String salt, String token) {
+ GrpcLoginQuery login = GrpcLoginQuery.newBuilder()
+ .setTenant(tenant)
+ .setName(name)
+ .setSalt(salt)
+ .setToken(token)
+ .build();
+ GrpcRTokenDTO response = tokenApiBlockingStub.checkValid(login);
+ return response.getResult().getOk();
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/UserGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/UserGrpcFacade.java
new file mode 100644
index 000000000..0c9ba4edd
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/UserGrpcFacade.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.auth.GrpcIdQuery;
+import io.github.pnoker.api.center.auth.GrpcRUserDTO;
+import io.github.pnoker.api.center.auth.UserApiGrpc;
+import io.github.pnoker.api.common.GrpcR;
+import io.github.pnoker.common.constant.service.AuthConstant;
+import io.github.pnoker.common.enums.ResponseEnum;
+import io.github.pnoker.common.exception.ServiceException;
+import io.github.pnoker.common.facade.api.UserFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
+import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcUserBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * gRPC {@link UserFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class UserGrpcFacade implements UserFacade {
+
+ @GrpcClient(AuthConstant.SERVICE_NAME)
+ private UserApiGrpc.UserApiBlockingStub userApiBlockingStub;
+
+ @Resource
+ private FacadeGrpcUserBuilder facadeGrpcUserBuilder;
+
+ @Override
+ public FacadeUserBO selectById(Long id) {
+ GrpcRUserDTO response = userApiBlockingStub.selectById(GrpcIdQuery.newBuilder().setId(id).build());
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectById");
+ return null;
+ }
+ return facadeGrpcUserBuilder.toFacadeBO(response.getData());
+ }
+
+ private void guardOrThrow(GrpcR result, String op) {
+ String code = result.getCode();
+ if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
+ log.debug("UserGrpcFacade.{} => no resource", op);
+ return;
+ }
+ throw new ServiceException("UserFacade." + op + " failed: [" + code + "] " + result.getMessage());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/UserLoginGrpcFacade.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/UserLoginGrpcFacade.java
new file mode 100644
index 000000000..a469c576a
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/UserLoginGrpcFacade.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc;
+
+import io.github.pnoker.api.center.auth.GrpcNameQuery;
+import io.github.pnoker.api.center.auth.GrpcRUserLoginDTO;
+import io.github.pnoker.api.center.auth.UserLoginApiGrpc;
+import io.github.pnoker.api.common.GrpcR;
+import io.github.pnoker.common.constant.service.AuthConstant;
+import io.github.pnoker.common.enums.ResponseEnum;
+import io.github.pnoker.common.exception.ServiceException;
+import io.github.pnoker.common.facade.api.UserLoginFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
+import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcUserLoginBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * gRPC {@link UserLoginFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class UserLoginGrpcFacade implements UserLoginFacade {
+
+ @GrpcClient(AuthConstant.SERVICE_NAME)
+ private UserLoginApiGrpc.UserLoginApiBlockingStub userLoginApiBlockingStub;
+
+ @Resource
+ private FacadeGrpcUserLoginBuilder facadeGrpcUserLoginBuilder;
+
+ @Override
+ public FacadeUserLoginBO selectByName(String name) {
+ GrpcRUserLoginDTO response = userLoginApiBlockingStub.selectByName(GrpcNameQuery.newBuilder().setName(name).build());
+ if (!response.getResult().getOk()) {
+ guardOrThrow(response.getResult(), "selectByName");
+ return null;
+ }
+ return facadeGrpcUserLoginBuilder.toFacadeBO(response.getData());
+ }
+
+ private void guardOrThrow(GrpcR result, String op) {
+ String code = result.getCode();
+ if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
+ log.debug("UserLoginGrpcFacade.{} => no resource", op);
+ return;
+ }
+ throw new ServiceException("UserLoginFacade." + op + " failed: [" + code + "] " + result.getMessage());
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcDeviceBuilder.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcDeviceBuilder.java
new file mode 100644
index 000000000..bbc13b45f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcDeviceBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.builder;
+
+import io.github.pnoker.api.center.manager.GrpcPageDeviceQuery;
+import io.github.pnoker.api.common.GrpcDeviceDTO;
+import io.github.pnoker.api.common.GrpcPage;
+import io.github.pnoker.common.constant.common.DefaultConstant;
+import io.github.pnoker.common.entity.common.Pages;
+import io.github.pnoker.common.entity.ext.DeviceExt;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
+import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
+import io.github.pnoker.common.optional.LongOptional;
+import io.github.pnoker.common.optional.StringOptional;
+import io.github.pnoker.common.utils.GrpcBuilderUtil;
+import io.github.pnoker.common.utils.JsonUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Converts between {@code dc3-common-facade-api} shapes and the protobuf types
+ * generated from {@code api/center/manager/manager_device.proto}.
+ *
+ * Hand-rolled rather than MapStruct because protobuf builders expose dozens of
+ * generated accessors ({@code mergeFrom}, {@code clearField}, {@code *Bytes}, ...)
+ * that would each need an explicit {@code @Mapping(target = "...", ignore = true)}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Component
+public class FacadeGrpcDeviceBuilder {
+
+ public GrpcPageDeviceQuery toGrpcPageQuery(FacadeDeviceQuery query) {
+ GrpcPageDeviceQuery.Builder builder = GrpcPageDeviceQuery.newBuilder();
+
+ Pages pages = Objects.isNull(query.getPage()) ? new Pages() : query.getPage();
+ GrpcPage.Builder page = GrpcPage.newBuilder()
+ .setCurrent(pages.getCurrent())
+ .setSize(pages.getSize());
+ builder.setPage(page);
+
+ LongOptional.ofNullable(query.getTenantId()).ifPresent(builder::setTenantId);
+ StringOptional.ofNullable(query.getDeviceName()).ifPresent(builder::setDeviceName);
+ StringOptional.ofNullable(query.getDeviceCode()).ifPresent(builder::setDeviceCode);
+ LongOptional.ofNullable(query.getDriverId()).ifPresent(builder::setDriverId);
+ LongOptional.ofNullable(query.getProfileId()).ifPresent(builder::setProfileId);
+ Optional.ofNullable(query.getEnableFlag()).ifPresentOrElse(
+ value -> builder.setEnableFlag(value.getIndex()),
+ () -> builder.setEnableFlag(DefaultConstant.DEFAULT_INT)
+ );
+
+ return builder.build();
+ }
+
+ public FacadeDeviceBO toFacadeBO(GrpcDeviceDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
+
+ FacadeDeviceBO bo = new FacadeDeviceBO();
+ GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
+
+ StringOptional.ofNullable(dto.getDeviceName()).ifPresent(bo::setDeviceName);
+ StringOptional.ofNullable(dto.getDeviceCode()).ifPresent(bo::setDeviceCode);
+ LongOptional.ofNullable(dto.getDriverId()).ifPresent(bo::setDriverId);
+ StringOptional.ofNullable(dto.getSignature()).ifPresent(bo::setSignature);
+ LongOptional.ofNullable(dto.getTenantId()).ifPresent(bo::setTenantId);
+
+ if (dto.getVersion() != DefaultConstant.DEFAULT_INT) {
+ bo.setVersion(dto.getVersion());
+ }
+
+ byte enableIndex = (byte) dto.getEnableFlag();
+ Optional.ofNullable(EnableFlagEnum.ofIndex(enableIndex)).ifPresent(bo::setEnableFlag);
+
+ StringOptional.ofNullable(dto.getDeviceExt())
+ .ifPresent(value -> bo.setDeviceExt(JsonUtil.parseObject(value, DeviceExt.class)));
+
+ if (dto.getProfileIdsCount() > 0) {
+ bo.setProfileIds(new ArrayList<>(dto.getProfileIdsList()));
+ }
+
+ return bo;
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcDriverBuilder.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcDriverBuilder.java
new file mode 100644
index 000000000..d3b8f3e51
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcDriverBuilder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.builder;
+
+import io.github.pnoker.api.center.manager.GrpcPageDriverQuery;
+import io.github.pnoker.api.common.GrpcDriverDTO;
+import io.github.pnoker.api.common.GrpcPage;
+import io.github.pnoker.common.constant.common.DefaultConstant;
+import io.github.pnoker.common.entity.common.Pages;
+import io.github.pnoker.common.entity.ext.DriverExt;
+import io.github.pnoker.common.enums.DriverTypeFlagEnum;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
+import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
+import io.github.pnoker.common.optional.LongOptional;
+import io.github.pnoker.common.optional.StringOptional;
+import io.github.pnoker.common.utils.GrpcBuilderUtil;
+import io.github.pnoker.common.utils.JsonUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Hand-rolled conversion between facade shapes and protobuf driver types.
+ *
+ * Driver uses {@code NULL_INT = -1} as the "not set" marker for
+ * {@code driverTypeFlag} (unlike Device, which piggy-backs on {@code DEFAULT_INT = 0}).
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Component
+public class FacadeGrpcDriverBuilder {
+
+ public GrpcPageDriverQuery toGrpcPageQuery(FacadeDriverQuery query) {
+ GrpcPageDriverQuery.Builder builder = GrpcPageDriverQuery.newBuilder();
+
+ Pages pages = Objects.isNull(query.getPage()) ? new Pages() : query.getPage();
+ GrpcPage.Builder page = GrpcPage.newBuilder()
+ .setCurrent(pages.getCurrent())
+ .setSize(pages.getSize());
+ builder.setPage(page);
+
+ LongOptional.ofNullable(query.getTenantId()).ifPresent(builder::setTenantId);
+ StringOptional.ofNullable(query.getDriverName()).ifPresent(builder::setDriverName);
+ StringOptional.ofNullable(query.getDriverCode()).ifPresent(builder::setDriverCode);
+ StringOptional.ofNullable(query.getServiceName()).ifPresent(builder::setServiceName);
+ StringOptional.ofNullable(query.getServiceHost()).ifPresent(builder::setServiceHost);
+ Optional.ofNullable(query.getDriverTypeFlag()).ifPresentOrElse(
+ value -> builder.setDriverTypeFlag(value.getIndex()),
+ () -> builder.setDriverTypeFlag(DefaultConstant.NULL_INT)
+ );
+ Optional.ofNullable(query.getEnableFlag()).ifPresentOrElse(
+ value -> builder.setEnableFlag(value.getIndex()),
+ () -> builder.setEnableFlag(DefaultConstant.DEFAULT_INT)
+ );
+
+ return builder.build();
+ }
+
+ public FacadeDriverBO toFacadeBO(GrpcDriverDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
+
+ FacadeDriverBO bo = new FacadeDriverBO();
+ GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
+
+ StringOptional.ofNullable(dto.getDriverName()).ifPresent(bo::setDriverName);
+ StringOptional.ofNullable(dto.getDriverCode()).ifPresent(bo::setDriverCode);
+ StringOptional.ofNullable(dto.getServiceName()).ifPresent(bo::setServiceName);
+ StringOptional.ofNullable(dto.getServiceHost()).ifPresent(bo::setServiceHost);
+ StringOptional.ofNullable(dto.getSignature()).ifPresent(bo::setSignature);
+ LongOptional.ofNullable(dto.getTenantId()).ifPresent(bo::setTenantId);
+
+ if (dto.getVersion() != DefaultConstant.DEFAULT_INT) {
+ bo.setVersion(dto.getVersion());
+ }
+
+ int driverType = dto.getDriverTypeFlag();
+ if (driverType != DefaultConstant.NULL_INT) {
+ Optional.ofNullable(DriverTypeFlagEnum.ofIndex((byte) driverType)).ifPresent(bo::setDriverTypeFlag);
+ }
+
+ Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
+
+ StringOptional.ofNullable(dto.getDriverExt())
+ .ifPresent(value -> bo.setDriverExt(JsonUtil.parseObject(value, DriverExt.class)));
+
+ return bo;
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcPointBuilder.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcPointBuilder.java
new file mode 100644
index 000000000..95dcbf6b6
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcPointBuilder.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.builder;
+
+import io.github.pnoker.api.center.manager.GrpcPagePointQuery;
+import io.github.pnoker.api.common.GrpcPage;
+import io.github.pnoker.api.common.GrpcPointDTO;
+import io.github.pnoker.common.constant.common.DefaultConstant;
+import io.github.pnoker.common.entity.common.Pages;
+import io.github.pnoker.common.entity.ext.PointExt;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.enums.PointTypeFlagEnum;
+import io.github.pnoker.common.enums.RwFlagEnum;
+import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
+import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
+import io.github.pnoker.common.optional.LongOptional;
+import io.github.pnoker.common.optional.StringOptional;
+import io.github.pnoker.common.utils.GrpcBuilderUtil;
+import io.github.pnoker.common.utils.JsonUtil;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Hand-rolled conversion between facade shapes and protobuf point types.
+ *
+ * {@code baseValue} / {@code multiple} are {@link BigDecimal} on the Java side but
+ * {@code double} on the wire — precision loss is inherited from the existing proto
+ * contract, not introduced here.
+ *
+ * Uses {@link DefaultConstant#NULL_INT NULL_INT} as "not set" for
+ * {@code pointTypeFlag} / {@code rwFlag} / {@code profileId}, matching
+ * {@code GrpcPointBuilder}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Component
+public class FacadeGrpcPointBuilder {
+
+ public GrpcPagePointQuery toGrpcPageQuery(FacadePointQuery query) {
+ GrpcPagePointQuery.Builder builder = GrpcPagePointQuery.newBuilder();
+
+ Pages pages = Objects.isNull(query.getPage()) ? new Pages() : query.getPage();
+ GrpcPage.Builder page = GrpcPage.newBuilder()
+ .setCurrent(pages.getCurrent())
+ .setSize(pages.getSize());
+ builder.setPage(page);
+
+ LongOptional.ofNullable(query.getTenantId()).ifPresent(builder::setTenantId);
+ StringOptional.ofNullable(query.getPointName()).ifPresent(builder::setPointName);
+ StringOptional.ofNullable(query.getPointCode()).ifPresent(builder::setPointCode);
+ LongOptional.ofNullable(query.getDeviceId()).ifPresent(builder::setDeviceId);
+
+ Optional.ofNullable(query.getPointTypeFlag()).ifPresentOrElse(
+ value -> builder.setPointTypeFlag(value.getIndex()),
+ () -> builder.setPointTypeFlag(DefaultConstant.NULL_INT)
+ );
+ Optional.ofNullable(query.getRwFlag()).ifPresentOrElse(
+ value -> builder.setRwFlag(value.getIndex()),
+ () -> builder.setRwFlag(DefaultConstant.NULL_INT)
+ );
+ Optional.ofNullable(query.getProfileId()).ifPresentOrElse(
+ builder::setProfileId,
+ () -> builder.setProfileId(DefaultConstant.NULL_INT)
+ );
+ Optional.ofNullable(query.getEnableFlag()).ifPresentOrElse(
+ value -> builder.setEnableFlag(value.getIndex()),
+ () -> builder.setEnableFlag(DefaultConstant.DEFAULT_INT)
+ );
+
+ return builder.build();
+ }
+
+ public FacadePointBO toFacadeBO(GrpcPointDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
+
+ FacadePointBO bo = new FacadePointBO();
+ GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
+
+ StringOptional.ofNullable(dto.getPointName()).ifPresent(bo::setPointName);
+ StringOptional.ofNullable(dto.getPointCode()).ifPresent(bo::setPointCode);
+ StringOptional.ofNullable(dto.getUnit()).ifPresent(bo::setUnit);
+ StringOptional.ofNullable(dto.getSignature()).ifPresent(bo::setSignature);
+ LongOptional.ofNullable(dto.getTenantId()).ifPresent(bo::setTenantId);
+ LongOptional.ofNullable(dto.getProfileId()).ifPresent(bo::setProfileId);
+
+ bo.setBaseValue(BigDecimal.valueOf(dto.getBaseValue()));
+ bo.setMultiple(BigDecimal.valueOf(dto.getMultiple()));
+ bo.setValueDecimal((byte) dto.getValueDecimal());
+
+ if (dto.getVersion() != DefaultConstant.DEFAULT_INT) {
+ bo.setVersion(dto.getVersion());
+ }
+
+ int pointType = dto.getPointTypeFlag();
+ if (pointType != DefaultConstant.NULL_INT) {
+ Optional.ofNullable(PointTypeFlagEnum.ofIndex((byte) pointType)).ifPresent(bo::setPointTypeFlag);
+ }
+
+ int rw = dto.getRwFlag();
+ if (rw != DefaultConstant.NULL_INT) {
+ Optional.ofNullable(RwFlagEnum.ofIndex((byte) rw)).ifPresent(bo::setRwFlag);
+ }
+
+ Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
+
+ StringOptional.ofNullable(dto.getPointExt())
+ .ifPresent(value -> bo.setPointExt(JsonUtil.parseObject(value, PointExt.class)));
+
+ return bo;
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcTenantBuilder.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcTenantBuilder.java
new file mode 100644
index 000000000..03a77e191
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcTenantBuilder.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.builder;
+
+import io.github.pnoker.api.center.auth.GrpcTenantDTO;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
+import io.github.pnoker.common.optional.StringOptional;
+import io.github.pnoker.common.utils.GrpcBuilderUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * {@code GrpcTenantDTO} → {@link FacadeTenantBO}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Component
+public class FacadeGrpcTenantBuilder {
+
+ public FacadeTenantBO toFacadeBO(GrpcTenantDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
+
+ FacadeTenantBO bo = new FacadeTenantBO();
+ GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
+
+ StringOptional.ofNullable(dto.getTenantName()).ifPresent(bo::setTenantName);
+ StringOptional.ofNullable(dto.getTenantCode()).ifPresent(bo::setTenantCode);
+ Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
+
+ return bo;
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcUserBuilder.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcUserBuilder.java
new file mode 100644
index 000000000..b3be72d37
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcUserBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.builder;
+
+import io.github.pnoker.api.center.auth.GrpcUserDTO;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
+import io.github.pnoker.common.optional.StringOptional;
+import io.github.pnoker.common.utils.GrpcBuilderUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * {@code GrpcUserDTO} → {@link FacadeUserBO}. {@code socialExt}/{@code identityExt}
+ * stay as opaque JSON strings — keeping the wire representation untouched.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Component
+public class FacadeGrpcUserBuilder {
+
+ public FacadeUserBO toFacadeBO(GrpcUserDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
+
+ FacadeUserBO bo = new FacadeUserBO();
+ GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
+
+ StringOptional.ofNullable(dto.getNickName()).ifPresent(bo::setNickName);
+ StringOptional.ofNullable(dto.getUserName()).ifPresent(bo::setUserName);
+ StringOptional.ofNullable(dto.getPhone()).ifPresent(bo::setPhone);
+ StringOptional.ofNullable(dto.getEmail()).ifPresent(bo::setEmail);
+ StringOptional.ofNullable(dto.getSocialExt()).ifPresent(bo::setSocialExt);
+ StringOptional.ofNullable(dto.getIdentityExt()).ifPresent(bo::setIdentityExt);
+
+ return bo;
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcUserLoginBuilder.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcUserLoginBuilder.java
new file mode 100644
index 000000000..e9ced1e69
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/builder/FacadeGrpcUserLoginBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.builder;
+
+import io.github.pnoker.api.center.auth.GrpcUserLoginDTO;
+import io.github.pnoker.common.enums.EnableFlagEnum;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
+import io.github.pnoker.common.optional.LongOptional;
+import io.github.pnoker.common.optional.StringOptional;
+import io.github.pnoker.common.utils.GrpcBuilderUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * {@code GrpcUserLoginDTO} → {@link FacadeUserLoginBO}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Component
+public class FacadeGrpcUserLoginBuilder {
+
+ public FacadeUserLoginBO toFacadeBO(GrpcUserLoginDTO dto) {
+ if (Objects.isNull(dto)) {
+ return null;
+ }
+
+ FacadeUserLoginBO bo = new FacadeUserLoginBO();
+ GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
+
+ StringOptional.ofNullable(dto.getLoginName()).ifPresent(bo::setLoginName);
+ LongOptional.ofNullable(dto.getUserId()).ifPresent(bo::setUserId);
+ LongOptional.ofNullable(dto.getUserPasswordId()).ifPresent(bo::setUserPasswordId);
+ Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
+
+ return bo;
+ }
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/config/GrpcFacadeAutoConfiguration.java b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/config/GrpcFacadeAutoConfiguration.java
new file mode 100644
index 000000000..9c274a007
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/java/io/github/pnoker/common/facade/grpc/config/GrpcFacadeAutoConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.grpc.config;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * Auto-configuration for the gRPC facade implementation. Active by default
+ * (when {@code dc3.facade.mode} is absent) or when explicitly set to
+ * {@code grpc}. Scans the facade-grpc package so the {@code @Component} beans
+ * are registered.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@AutoConfiguration
+@ConditionalOnProperty(name = "dc3.facade.mode", havingValue = "grpc", matchIfMissing = true)
+@ComponentScan("io.github.pnoker.common.facade.grpc")
+public class GrpcFacadeAutoConfiguration {
+}
diff --git a/dc3-common/dc3-common-facade-grpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dc3-common/dc3-common-facade-grpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..e010b2397
--- /dev/null
+++ b/dc3-common/dc3-common-facade-grpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,18 @@
+#
+# Copyright 2016-present the IoT DC3 original author or authors.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+io.github.pnoker.common.facade.grpc.config.GrpcFacadeAutoConfiguration
diff --git a/dc3-common/dc3-common-facade-local/pom.xml b/dc3-common/dc3-common-facade-local/pom.xml
new file mode 100644
index 000000000..a2ada00dd
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ 4.0.0
+
+
+ io.github.pnoker
+ dc3-common
+ 2026.4.19
+
+
+ ${project.artifactId}
+ dc3-common-facade-local
+ 2026.4.19
+ jar
+
+ 2016
+ https://github.com/pnoker/iot-dc3
+
+ Local (in-process) implementation of the Facade API, selected when
+ dc3.facade.mode=local. Directly invokes manager services without gRPC.
+
+
+
+
+
+
+ io.github.pnoker
+ dc3-common-facade-api
+
+
+ io.github.pnoker
+ dc3-common-auth
+
+
+ io.github.pnoker
+ dc3-common-manager
+
+
+
+
+
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/DeviceLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/DeviceLocalFacade.java
new file mode 100644
index 000000000..12ead1c5a
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/DeviceLocalFacade.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.github.pnoker.common.facade.api.DeviceFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
+import io.github.pnoker.common.facade.local.builder.FacadeDeviceBuilder;
+import io.github.pnoker.common.manager.entity.bo.DeviceBO;
+import io.github.pnoker.common.manager.entity.query.DeviceQuery;
+import io.github.pnoker.common.manager.service.DeviceService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * In-process implementation: routes each call straight into {@link DeviceService}.
+ *
+ * Selected when {@code dc3.facade.mode=local}. Carries zero serialization cost
+ * — the same JVM handles both caller and service.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class DeviceLocalFacade implements DeviceFacade {
+
+ @Resource
+ private DeviceService deviceService;
+
+ @Resource
+ private FacadeDeviceBuilder facadeDeviceBuilder;
+
+ @Override
+ public FacadeDeviceBO selectById(Long id) {
+ DeviceBO managerBO = deviceService.selectById(id);
+ return Objects.isNull(managerBO) ? null : facadeDeviceBuilder.toFacadeBO(managerBO);
+ }
+
+ @Override
+ public FacadePage selectByPage(FacadeDeviceQuery query) {
+ DeviceQuery managerQuery = facadeDeviceBuilder.toManagerQuery(query);
+ Page page = deviceService.selectByPage(managerQuery);
+ if (Objects.isNull(page)) {
+ return FacadePage.empty();
+ }
+
+ List records = page.getRecords().stream()
+ .map(facadeDeviceBuilder::toFacadeBO)
+ .toList();
+ return new FacadePage<>(page.getCurrent(), page.getSize(), page.getTotal(), page.getPages(), records);
+ }
+
+ @Override
+ public List selectByProfileId(Long profileId) {
+ List list = deviceService.selectByProfileId(profileId);
+ if (Objects.isNull(list) || list.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return list.stream().map(facadeDeviceBuilder::toFacadeBO).toList();
+ }
+
+ @Override
+ public List selectByDriverId(Long driverId) {
+ List list = deviceService.selectByDriverId(driverId);
+ if (Objects.isNull(list) || list.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return list.stream().map(facadeDeviceBuilder::toFacadeBO).toList();
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/DriverLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/DriverLocalFacade.java
new file mode 100644
index 000000000..375497a42
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/DriverLocalFacade.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.github.pnoker.common.facade.api.DriverFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
+import io.github.pnoker.common.facade.local.builder.FacadeDriverBuilder;
+import io.github.pnoker.common.manager.entity.bo.DriverBO;
+import io.github.pnoker.common.manager.entity.query.DriverQuery;
+import io.github.pnoker.common.manager.service.DriverService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * In-process DriverFacade implementation.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class DriverLocalFacade implements DriverFacade {
+
+ @Resource
+ private DriverService driverService;
+
+ @Resource
+ private FacadeDriverBuilder facadeDriverBuilder;
+
+ @Override
+ public FacadeDriverBO selectById(Long id) {
+ DriverBO managerBO = driverService.selectById(id);
+ return Objects.isNull(managerBO) ? null : facadeDriverBuilder.toFacadeBO(managerBO);
+ }
+
+ @Override
+ public FacadePage selectByPage(FacadeDriverQuery query) {
+ DriverQuery managerQuery = facadeDriverBuilder.toManagerQuery(query);
+ Page page = driverService.selectByPage(managerQuery);
+ if (Objects.isNull(page)) {
+ return FacadePage.empty();
+ }
+
+ List records = page.getRecords().stream()
+ .map(facadeDriverBuilder::toFacadeBO)
+ .toList();
+ return new FacadePage<>(page.getCurrent(), page.getSize(), page.getTotal(), page.getPages(), records);
+ }
+
+ @Override
+ public FacadeDriverBO selectByDeviceId(Long deviceId) {
+ DriverBO managerBO = driverService.selectByDeviceId(deviceId);
+ return Objects.isNull(managerBO) ? null : facadeDriverBuilder.toFacadeBO(managerBO);
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/PointLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/PointLocalFacade.java
new file mode 100644
index 000000000..e4f6dfb00
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/PointLocalFacade.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.github.pnoker.common.facade.api.PointFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
+import io.github.pnoker.common.facade.entity.common.FacadePage;
+import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
+import io.github.pnoker.common.facade.local.builder.FacadePointBuilder;
+import io.github.pnoker.common.manager.entity.bo.PointBO;
+import io.github.pnoker.common.manager.entity.query.PointQuery;
+import io.github.pnoker.common.manager.service.PointService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * In-process PointFacade implementation.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class PointLocalFacade implements PointFacade {
+
+ @Resource
+ private PointService pointService;
+
+ @Resource
+ private FacadePointBuilder facadePointBuilder;
+
+ @Override
+ public FacadePointBO selectById(Long id) {
+ PointBO managerBO = pointService.selectById(id);
+ return Objects.isNull(managerBO) ? null : facadePointBuilder.toFacadeBO(managerBO);
+ }
+
+ @Override
+ public FacadePage selectByPage(FacadePointQuery query) {
+ PointQuery managerQuery = facadePointBuilder.toManagerQuery(query);
+ Page page = pointService.selectByPage(managerQuery);
+ if (Objects.isNull(page)) {
+ return FacadePage.empty();
+ }
+
+ List records = page.getRecords().stream()
+ .map(facadePointBuilder::toFacadeBO)
+ .toList();
+ return new FacadePage<>(page.getCurrent(), page.getSize(), page.getTotal(), page.getPages(), records);
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/TenantLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/TenantLocalFacade.java
new file mode 100644
index 000000000..e4ead6229
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/TenantLocalFacade.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import io.github.pnoker.common.auth.entity.bo.TenantBO;
+import io.github.pnoker.common.auth.service.TenantService;
+import io.github.pnoker.common.facade.api.TenantFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
+import io.github.pnoker.common.facade.local.builder.FacadeTenantBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * In-process {@link TenantFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class TenantLocalFacade implements TenantFacade {
+
+ @Resource
+ private TenantService tenantService;
+
+ @Resource
+ private FacadeTenantBuilder facadeTenantBuilder;
+
+ @Override
+ public FacadeTenantBO selectByCode(String code) {
+ TenantBO bo = tenantService.selectByCode(code);
+ return Objects.isNull(bo) ? null : facadeTenantBuilder.toFacadeBO(bo);
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/TokenLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/TokenLocalFacade.java
new file mode 100644
index 000000000..dd8f73aa7
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/TokenLocalFacade.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import io.github.pnoker.common.auth.biz.TokenService;
+import io.github.pnoker.common.auth.entity.bean.TokenValid;
+import io.github.pnoker.common.facade.api.TokenFacade;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * In-process {@link TokenFacade}. Collapses the three-way outcome of
+ * {@code TokenService.checkValid} (null / invalid / valid) into a boolean.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class TokenLocalFacade implements TokenFacade {
+
+ @Resource
+ private TokenService tokenService;
+
+ @Override
+ public boolean checkValid(String tenant, String name, String salt, String token) {
+ TokenValid entity = tokenService.checkValid(name, salt, token, tenant);
+ return Objects.nonNull(entity) && entity.isValid();
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/UserLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/UserLocalFacade.java
new file mode 100644
index 000000000..0007a1420
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/UserLocalFacade.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import io.github.pnoker.common.auth.entity.bo.UserBO;
+import io.github.pnoker.common.auth.service.UserService;
+import io.github.pnoker.common.facade.api.UserFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
+import io.github.pnoker.common.facade.local.builder.FacadeUserBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * In-process {@link UserFacade}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class UserLocalFacade implements UserFacade {
+
+ @Resource
+ private UserService userService;
+
+ @Resource
+ private FacadeUserBuilder facadeUserBuilder;
+
+ @Override
+ public FacadeUserBO selectById(Long id) {
+ UserBO bo = userService.selectById(id);
+ return Objects.isNull(bo) ? null : facadeUserBuilder.toFacadeBO(bo);
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/UserLoginLocalFacade.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/UserLoginLocalFacade.java
new file mode 100644
index 000000000..98194a6f6
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/UserLoginLocalFacade.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local;
+
+import io.github.pnoker.common.auth.entity.bo.UserLoginBO;
+import io.github.pnoker.common.auth.service.UserLoginService;
+import io.github.pnoker.common.facade.api.UserLoginFacade;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
+import io.github.pnoker.common.facade.local.builder.FacadeUserLoginBuilder;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * In-process {@link UserLoginFacade}. Matches {@code UserLoginServer} which
+ * also calls {@code selectByLoginName(name, false)}.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Slf4j
+@Component
+public class UserLoginLocalFacade implements UserLoginFacade {
+
+ @Resource
+ private UserLoginService userLoginService;
+
+ @Resource
+ private FacadeUserLoginBuilder facadeUserLoginBuilder;
+
+ @Override
+ public FacadeUserLoginBO selectByName(String name) {
+ UserLoginBO bo = userLoginService.selectByLoginName(name, false);
+ return Objects.isNull(bo) ? null : facadeUserLoginBuilder.toFacadeBO(bo);
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeDeviceBuilder.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeDeviceBuilder.java
new file mode 100644
index 000000000..c04591b55
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeDeviceBuilder.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.builder;
+
+import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
+import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
+import io.github.pnoker.common.manager.entity.bo.DeviceBO;
+import io.github.pnoker.common.manager.entity.query.DeviceQuery;
+import io.github.pnoker.common.utils.MapStructUtil;
+import org.mapstruct.Mapper;
+
+/**
+ * Converts between the facade-api shapes and {@code dc3-common-manager} internals.
+ * Field sets are aligned by design, so MapStruct auto-maps every property.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
+public interface FacadeDeviceBuilder {
+
+ DeviceQuery toManagerQuery(FacadeDeviceQuery facadeQuery);
+
+ FacadeDeviceBO toFacadeBO(DeviceBO managerBO);
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeDriverBuilder.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeDriverBuilder.java
new file mode 100644
index 000000000..6cd2ed590
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeDriverBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.builder;
+
+import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
+import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
+import io.github.pnoker.common.manager.entity.bo.DriverBO;
+import io.github.pnoker.common.manager.entity.query.DriverQuery;
+import io.github.pnoker.common.utils.MapStructUtil;
+import org.mapstruct.Mapper;
+
+/**
+ * FacadeDriver ↔ manager DriverBO/Query mapper. Field sets are aligned.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
+public interface FacadeDriverBuilder {
+
+ DriverQuery toManagerQuery(FacadeDriverQuery facadeQuery);
+
+ FacadeDriverBO toFacadeBO(DriverBO managerBO);
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadePointBuilder.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadePointBuilder.java
new file mode 100644
index 000000000..2d613973c
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadePointBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.builder;
+
+import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
+import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
+import io.github.pnoker.common.manager.entity.bo.PointBO;
+import io.github.pnoker.common.manager.entity.query.PointQuery;
+import io.github.pnoker.common.utils.MapStructUtil;
+import org.mapstruct.Mapper;
+
+/**
+ * FacadePoint ↔ manager PointBO/Query mapper.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
+public interface FacadePointBuilder {
+
+ PointQuery toManagerQuery(FacadePointQuery facadeQuery);
+
+ FacadePointBO toFacadeBO(PointBO managerBO);
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeTenantBuilder.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeTenantBuilder.java
new file mode 100644
index 000000000..e0c58e178
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeTenantBuilder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.builder;
+
+import io.github.pnoker.common.auth.entity.bo.TenantBO;
+import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
+import io.github.pnoker.common.utils.MapStructUtil;
+import org.mapstruct.Mapper;
+
+/**
+ * FacadeTenant ↔ auth TenantBO mapper. {@code tenantExt} is absent from the
+ * facade shape and therefore dropped by MapStruct.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
+public interface FacadeTenantBuilder {
+
+ FacadeTenantBO toFacadeBO(TenantBO authBO);
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeUserBuilder.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeUserBuilder.java
new file mode 100644
index 000000000..392befc1f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeUserBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.builder;
+
+import io.github.pnoker.common.auth.entity.bo.UserBO;
+import io.github.pnoker.common.entity.ext.UserIdentityExt;
+import io.github.pnoker.common.entity.ext.UserSocialExt;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
+import io.github.pnoker.common.utils.JsonUtil;
+import io.github.pnoker.common.utils.MapStructUtil;
+import org.mapstruct.AfterMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+
+import java.util.Objects;
+
+/**
+ * FacadeUser ↔ auth UserBO mapper.
+ *
+ * {@code UserBO} carries typed {@code UserSocialExt}/{@code UserIdentityExt}
+ * objects, while the facade contract (matching {@code GrpcUserDTO}) carries
+ * their JSON forms, so these two fields need an explicit conversion.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
+public interface FacadeUserBuilder {
+
+ @Mapping(target = "socialExt", ignore = true)
+ @Mapping(target = "identityExt", ignore = true)
+ FacadeUserBO toFacadeBO(UserBO authBO);
+
+ @AfterMapping
+ default void afterProcess(UserBO authBO, @MappingTarget FacadeUserBO facadeBO) {
+ UserSocialExt social = authBO.getSocialExt();
+ if (Objects.nonNull(social)) {
+ facadeBO.setSocialExt(JsonUtil.toJsonString(social));
+ }
+
+ UserIdentityExt identity = authBO.getIdentityExt();
+ if (Objects.nonNull(identity)) {
+ facadeBO.setIdentityExt(JsonUtil.toJsonString(identity));
+ }
+ }
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeUserLoginBuilder.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeUserLoginBuilder.java
new file mode 100644
index 000000000..21a92d494
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/builder/FacadeUserLoginBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.builder;
+
+import io.github.pnoker.common.auth.entity.bo.UserLoginBO;
+import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
+import io.github.pnoker.common.utils.MapStructUtil;
+import org.mapstruct.Mapper;
+
+/**
+ * FacadeUserLogin ↔ auth UserLoginBO mapper.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
+public interface FacadeUserLoginBuilder {
+
+ FacadeUserLoginBO toFacadeBO(UserLoginBO authBO);
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/config/LocalFacadeAutoConfiguration.java b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/config/LocalFacadeAutoConfiguration.java
new file mode 100644
index 000000000..fe362cc1f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/java/io/github/pnoker/common/facade/local/config/LocalFacadeAutoConfiguration.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present the IoT DC3 original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package io.github.pnoker.common.facade.local.config;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * Auto-configuration for the local facade implementation. Active only when
+ * {@code dc3.facade.mode=local}. Scans the facade-local package so the
+ * {@code @Component}/{@code @Mapper} beans are registered.
+ *
+ * @author pnoker
+ * @since 2026.4.19
+ */
+@AutoConfiguration
+@ConditionalOnProperty(name = "dc3.facade.mode", havingValue = "local")
+@ComponentScan("io.github.pnoker.common.facade.local")
+public class LocalFacadeAutoConfiguration {
+}
diff --git a/dc3-common/dc3-common-facade-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/dc3-common/dc3-common-facade-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..426fa161f
--- /dev/null
+++ b/dc3-common/dc3-common-facade-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,18 @@
+#
+# Copyright 2016-present the IoT DC3 original author or authors.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+io.github.pnoker.common.facade.local.config.LocalFacadeAutoConfiguration
diff --git a/dc3-common/pom.xml b/dc3-common/pom.xml
index c077b02d5..91d466739 100644
--- a/dc3-common/pom.xml
+++ b/dc3-common/pom.xml
@@ -82,6 +82,9 @@
dc3-common-data
dc3-common-driver
dc3-common-exception
+ dc3-common-facade-api
+ dc3-common-facade-grpc
+ dc3-common-facade-local
dc3-common-gateway
dc3-common-log
dc3-common-manager
@@ -213,6 +216,21 @@
dc3-common-exception
${dc3.version}