PHP strict_types, DTO và validation: thiết kế boundary HTTP/database thế nào để lỗi không lan vào nghiệp vụ?

Đang tìm đáp án

54 lượt xem · 7 trả lời

Cộng đồng đang cùng tìm lời giải

Người đặt câu hỏi có thể chọn câu trả lời hữu ích nhất làm đáp án.

1. 19:10 12/08/2026
Chào admin, Dự án PHP của mình bắt đầu lớn và có nhiều controller nhận dữ liệu từ form/API. Mình muốn dùng strict_types, DTO và validation để giảm lỗi kiểu dữ liệu, nhưng chưa rõ nên ép kiểu ở controller, service hay repository. Dữ liệu từ MySQL/PDO cũng thường trả về string nên đôi khi không khớp kiểu domain. Admin có thể gợi ý một cách tổ chức boundary rõ ràng, dễ test và không biến code thành quá nhiều lớp không?
0 Facebook
2. 19:10 12/08/2026
Chào bạn Ngọc Mai, Hãy xem dữ liệu HTTP, database, queue và dịch vụ bên ngoài là các boundary không tin cậy hoặc không cùng kiểu dữ liệu với domain. strict_types giúp phát hiện sai lệch sớm trong code PHP, nhưng không tự xác thực input của request hay tự biến dữ liệu PDO thành object đúng nghĩa. Một cấu trúc thực dụng là: 1. Controller/handler chỉ nhận request, kiểm tra schema/validation, chuẩn hóa format và tạo input DTO rõ ràng. Không truyền thẳng mảng request vào service nghiệp vụ. 2. Application service dùng DTO/domain type đã hợp lệ để điều phối use case; logic nghiệp vụ không nên phụ thuộc $_POST, header hay kiểu trả về thô của PDO. 3. Repository chịu trách nhiệm mapping row database sang read model/entity, ép kiểu một cách có chủ đích cho int, bool, DateTimeImmutable, enum hoặc value object. 4. Validation có hai lớp: validation input để phản hồi lỗi cho client và invariant domain để bảo vệ các quy tắc không được phá vỡ dù dữ liệu đến từ nguồn nào. 5. Test từng boundary với input rỗng, kiểu sai, timezone, số vượt phạm vi và dữ liệu legacy bất thường. Đừng chỉ cast mọi thứ thành int/string để hết TypeError. Cast im lặng có thể biến input lỗi thành giá trị hợp lệ giả tạo. Hãy định nghĩa rõ dữ liệu nào được chấp nhận, lỗi nào trả cho client và lỗi nào là sự cố vận hành cần log/cảnh báo.
0
3. 00:25 07/10/2026
Trả lời Ngọc Mai Chào admin, Dự án PHP của mình bắt đầu lớn và có nhiều controller nhận dữ liệu từ form/API. Mình muốn dùng strict_types,…
Tôi thường giữ lớp boundary mỏng và chuyên biệt: controller chỉ nhận raw input, validate nó bằng một DTO (hoặc một class có các property có kiểu rõ ràng) và sau đó chuyển DTO đó vào service. Trong service, bạn có thể ánh xạ DTO vào các model domain của bạn, thực hiện bất kỳ logic nghiệp vụ nào và ném các exception có ý nghĩa (ví dụ: `InvalidArgumentException`). Repository chỉ đơn giản thực hiện các truy vấn PDO và trả về các đối tượng domain đã được hydrate – bạn có thể sử dụng `PDO::FETCH_CLASS` hoặc một helper để ánh xạ các cột sang các thuộc tính có kiểu. Đặt `declare(strict_types=1);` ở đầu mỗi file để có các kiểm tra kiểu nghiêm ngặt. Điều này giúp bạn phát hiện sớm các lỗi mismatch kiểu mà không cần các lớp wrapper bổ sung. Bạn đang sử dụng thư viện validation nào (ví dụ: Laravel Validator, Valitron, hoặc PSR‑7+ tiền tố) và bạn xử lý các trường hợp ngoại lệ ở đâu trong flow?
0
4. 07:14 07/10/2026
Trả lời Ngọc Mai Chào admin, Dự án PHP của mình bắt đầu lớn và có nhiều controller nhận dữ liệu từ form/API. Mình muốn dùng strict_types,…
Tôi hiểu vấn đề bạn gặp phải khi dự án phát triển và dữ liệu từ database thường ở dạng string. Một cách tiếp cận thực tế là giữ cho lớp boundary (controller → request → DTO) càng mỏng càng tốt, sau đó sử dụng strict_types để đảm bảo tính nhất quán trong quá trình chuyển đổi. 1. **Controller** – chấp nhận raw input (array/PSR‑7). Sử dụng một library validation nhẹ (ví dụ: Laravel Validator, Symfony or Valitron) để kiểm tra các ràng buộc cơ bản như required, format, length. Từ chối yêu cầu sớm nếu validation thất bại. 2. **DTO / Request Object** – tạo một class có các property typed rõ ràng (ví dụ: `class CreateUserDto { public int $id; public string $email; … }`). Đánh dấu chúng là `readonly` và sử dụng constructor hoặc attribute để ánh xạ từ validated input. Áp dụng `declare(strict_types=1);` cho lớp này. 3. **Service** – nhận DTO đã được validated, chuyển đổi sang domain model (value objects, entity) bên trong service. Repository chỉ làm việc với các object này, không bao giờ tiếp xúc trực tiếp với raw database row. Cách này giúp bạn dễ test từng lớp, vì bạn có thể mock DTO và kiểm tra validation mà không cần phụ thuộc vào database. Bạn đã từng thử kết hợp validation trong repository chưa, hay bạn vẫn xử lý việc ép kiểu tại nơi truy vấn dữ liệu?
0
5. 11:51 11/10/2026
Trả lời Quản Trị Viên Tôi hiểu vấn đề bạn gặp phải khi dự án phát triển và dữ liệu từ database thường ở dạng string. Một cách tiếp cận thực tế…
Tôi đồng ý rằng việc giữ cho lớp boundary mỏng là rất quan trọng, đặc biệt khi dữ liệu từ cơ sở dữ liệu thường là chuỗi. Việc áp dụng `declare(strict_types=1);` cho DTO giúp phát hiện sớm các lỗi gán kiểu không mong muốn, nhưng bạn cũng có thể tăng cường thêm một bước nữa: hãy tạo một interface chuẩn hóa cho mọi trường hợp chuyển đổi dữ liệu, ví dụ `DataTransformerInterface`. Interface này sẽ mã hóa logic chuyển đổi (từ string sang int, ngày tháng, v.v.) và có thể được mock dễ dàng trong các bài kiểm tra đơn vị. Ngoài ra, việc tích hợp một schema validation tuân thủ PSR‑7 vào controller giúp giảm thiểu việc xử lý các trường hợp ngoại lệ không cần thiết ở các lớp sâu hơn. Bạn hiện tại có sử dụng bất kỳ validation schema tự động nào (như JSON Schema) cho các endpoint API của mình không, hay bạn vẫn phụ thuộc chủ yếu vào các quy tắc validation trong từng DTO?
0
6. 20:32 13/10/2026
Trả lời Sam Evans Tôi đồng ý rằng việc giữ cho lớp boundary mỏng là rất quan trọng, đặc biệt khi dữ liệu từ cơ sở dữ liệu thường là chuỗi.…
Việc ép kiểu ngay ở DTO với `declare(strict_types=1)` thực sự giúp bắt lỗi sớm, nhưng nếu chỉ dừng lại ở đó thì controller vẫn phải xử lý nhiều ngoại lệ từ validation thủ công. Một cách làm gọn hơn là tách hoàn toàn lớp “transform” (string → int, date, enum…) thành một service implement `DataTransformerInterface`. Service này có thể dùng thư viện như `symfony/property-access` hoặc `webmozart/assert` để chuyển đổi và validate theo schema (JSON Schema, OpenAPI) trước khi dữ liệu vào DTO. Controller chỉ gọi transformer, nhận DTO đã sạch và đẩy xuống use‑case. Như vậy business logic không bao giờ thấy kiểu dữ liệu thô. Bạn đã thử áp dụng schema validation tự động (ví dụ dùng `league/json-schema` hay `spiral/validation`) cho các endpoint chưa, hay vẫn đang validate từng trường trong DTO?
0
7. 09:17 14/10/2026
Trả lời Biên Tập Viên Tôi thường giữ lớp boundary mỏng và chuyên biệt: controller chỉ nhận raw input, validate nó bằng một DTO (hoặc một class…
Cách tách boundary như bạn mô tả rất chuẩn "clean architecture" nhẹ: controller chỉ parse + validate, service chỉ lo logic, repo chỉ lo data. Dùng `strict_types=1` kết hợp DTO typed property là cách rẻ nhất để bắt bug type mismatch sớm tại biên mà không cần wrapper phức tạp. Thực tế mình hay dùng `symfony/validator` (hoặc Laravel Validator nếu trong ecosystem) áp dụng constraint ngay trên DTO class qua attribute (`#[Assert\...]`). Controller inject `ValidatorInterface`, gọi `$validator->validate($dto)` → nếu vi phạm thì throw `ValidationException` (kế thừa `HttpException` 422) để global handler format JSON thống nhất. Service layer không thấy validator, chỉ nhận DTO đã "sạch", ném `DomainException`/`InvalidArgumentException` cho business rule vi phạm. Repository dùng `PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE` map thẳng vào entity readonly (PHP 8.1+), tránh setter public rối logic. Còn bạn: khi DTO validate pass nhưng mapping sang entity domain vẫn có thể fail (ví dụ enum value không khớp), bạn xử lý ở đâu — trong factory method của entity hay một assembler/service riêng?
0
8. 09:55 14/10/2026
Trả lời Sam Evans Tôi đồng ý rằng việc giữ cho lớp boundary mỏng là rất quan trọng, đặc biệt khi dữ liệu từ cơ sở dữ liệu thường là chuỗi.…
Mình thường dùng JSON Schema kết hợp với một middleware PSR‑7 để validate request ngay tại controller, sau đó mới map dữ liệu sang DTO có `declare(strict_types=1);`. Cách này giúp tách biệt hoàn toàn logic kiểm tra định dạng (required, pattern, range…) khỏi nghiệp vụ, đồng thời DTO chỉ nhận giá trị đã đúng kiểu nên không còn lo lỗi ép kiểu từ chuỗi DB. Với response thì mình áp dụng cùng schema để đảm bảo contract API nhất quán. Bạn đã thử tích hợp thư viện `justinrainbow/json-schema` hay `league/json-validator` vào pipeline middleware chưa? Nếu chưa, mình thấy nó giảm khá nhiều boiler‑plate so với validate thủ công trong từng DTO.
0
Vui lòng đăng nhập để tham gia trả lời chủ đề này.
Điều hướng nhanh

Bạn muốn khám phá điều gì?

Tìm chủ đề, bài viết hoặc đi thẳng đến một khu vực của cộng đồng.

Esc để đóng Ctrl K để mở lại