Tầm nhìn Kiến trúc

Nguyên tắc chủ đạo

Kiến trúc dựa trên nguyên lý tách biệt các mối quan tâm (separation of concerns):

  • Nội dung: các tệp AsciiDoc được cấu trúc với siêu dữ liệu phong phú

  • Giới thiệu: các template Thymeleaf có thể tái sử dụng

  • Dữ liệu: mô hình được JBake trích xuất tự động từ các thuộc tính AsciiDoc

  • Phong cách: Bootstrap 5 để đảm bảo tính nhất quán về mặt hình ảnh

Lựa chọn chiến lược: AsciiDoc cho portfolio

Tại sao lại là AsciiDoc?

Tiêu chí Lợi thế

Tính nhất quán

Cùng định dạng với các bài viết blog

Siêu dữ liệu

Các thuộc tính có cấu trúc và có thể mở rộng (project-*)

Khả năng bảo trì

Chỉnh sửa văn bản đơn giản, có thể quản lý phiên bản bằng Git

Tính linh hoạt

Có thể chứa nội dung phong phú (bảng, mã, hình ảnh)

Tạo mẫu

JBake tự động trích xuất các thuộc tính cho Thymeleaf

Mô hình dữ liệu

Mỗi dự án portfolio là một tài liệu AsciiDoc với:

  • Siêu dữ liệu tiêu đề: thông tin có cấu trúc (khách hàng, thời lượng, công nghệ, v.v.)

  • Nội dung tài liệu : mô tả chi tiết, thách thức, giải pháp, kết quả

  • Thuộc tính tùy chỉnh: có tiền tố`project-*`cho việc trích xuất tự động

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

class ProjetAsciiDoc {
  +titre: String
  +jbake-type: "dự án"
  +jbake-status: published|draft
  +jbake-date: Date
  +jbake-tags: List<String>
  --
  +project-category: String
  +project-client: String
  +project-duration: String
  +project-role: String
  +project-thumbnail: String
  +project-gallery: List<String>
  +project-tech-stack: String
  +project-highlights: List<String>
  +project-demo-url: String
  +project-github-url: String
  --
  +body: HTML (converti)
}

class JBakeEngine {
  +parse(asciidoc)
  +extractMetadata()
  +convertToHTML()
}

class ThymeleafTemplate {
  +portfolio.html
  +project.html
  +partials/project-card.html
}

class PortfolioPage {
  +published_projects: List
  +filtres: categories
}

ProjetAsciiDoc --> JBakeEngine : phân tích
JBakeEngine --> ThymeleafTemplate : cung cấp dữ liệu
ThymeleafTemplate --> PortfolioPage : tạo ra
@enduml

Các trường hợp sử dụng chi tiết

UC1 : Thêm một mục vào danh mục hồ sơ (portfolio)

Lưu lượng định mức

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant "Biên tập\nVăn bản" as editor
participant "Tệp\nAsciiDoc" as file
participant "JBake\nCông cụ" as jbake
participant "Trang web\ntĩnh" as site

dev -> editor : Tạo file mới
activate editor
editor -> file : portfolio/nouveau-projet.adoc
activate file

dev -> editor : Viết tiêu đề với metadata\n(titre, type=project, status=published,\nattributs project-*)
dev -> editor : Viết nội dung câu chuyện\n(bối cảnh, thách thức, giải pháp)
dev -> editor : Thêm hình ảnh vào assets/img/portfolio/

editor -> file : Lưu
deactivate editor

dev -> jbake : Thực hiện build (jbake -b)
activate jbake
jbake -> file : Đọc và phân tích
jbake -> jbake : Trích xuất siêu dữ liệu
jbake -> jbake : Chuyển đổi AsciiDoc → HTML
jbake -> jbake : Áp dụng mẫu project.html
jbake -> jbake : Thêm vào danh sách portfolio.html
jbake -> site : Tạo các trang tĩnh
deactivate jbake

dev -> site : Kiểm tra kết quả
activate site
site --> dev : Hiển thị dự án
deactivate site
@enduml

Mẫu tệp cần tạo

Nhà phát triển tạo`content/portfolio/nom-projet.adoc`với một cấu trúc tiêu chuẩn hóa:

  • Tiêu đề với tất cả các thuộc tính bắt buộc

  • Các phần chuẩn hóa (Bối cảnh, Thách thức, Giải pháp, Kết quả)

  • Đặt tên hình ảnh nhất quán

Các điểm xác thực

  • Các thuộc tính bắt buộc hiện có (jbake-type, jbake-status, project-thumbnail)

  • Các hình ảnh được tham chiếu tồn tại trong`assets/img/portfolio/`

  • Quá trình build JBake thành công mà không có lỗi

  • Dự án xuất hiện trên trang portfolio

  • Trang chi tiết của dự án hiển thị chính xác

UC2 : Xóa một phần tử khỏi portfolio

Luồng định mức

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant "Hệ thống\nTập tin" as fs
participant "JBake\nĐộng cơ" as jbake
participant "Trang web\ntĩnh" as site
database "Bộ nhớ cache\nJBake" as cache

dev -> fs : Xóa portfolio/projet-ancien.adoc
activate fs
fs --> dev : Tệp đã bị xóa
deactivate fs

dev -> fs : **(Tuỳ chọn) Xóa hình ảnh liên quan\nassets/img/portfolio/projet-ancien-***
activate fs
fs --> dev : Hình ảnh đã bị xóa
deactivate fs

dev -> cache : Xóa cache JBake
activate cache
cache --> dev : Bộ nhớ đệm đã được xóa
deactivate cache

dev -> jbake : Xây dựng lại hoàn toàn (jbake -b)
activate jbake
jbake -> jbake : Máy quét nội dung/portfolio/
jbake -> jbake : Dự án không tồn tại → không được tạo
jbake -> jbake : Tái tạo portfolio.html\n(không có dự án đã bị xóa)
jbake -> site : Triển khai bản dựng mới
deactivate jbake

dev -> site : Kiểm tra
activate site
site --> dev : Dự án không có trong danh sách
deactivate site
@enduml

Chiến lược thay thế: lưu trữ

Thay vì xóa vĩnh viễn, có khả năng tạo một thư mục`content/portfolio/archive/`Vui lòng cung cấp nội dung cần dịch.

  • Di chuyển tệp thay vì xóa nó

  • Cho phép khôi phục dễ dàng

  • Giữ lịch sử Git rõ ràng hơn

Dọn dẹp tài nguyên

  • Kiểm tra các hình ảnh mồ côi trong`assets/img/portfolio/`

  • Xóa các hình ảnh không được tham chiếu bởi các dự án khác

  • Xóa bộ nhớ đệm JBake để tránh các tham chiếu ma

UC3 : Chuyển sang chưa xuất bản (draft)

Flux định mức

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant "Tệp\nAsciiDoc" as file
participant "JBake\nĐộng cơ" as jbake
participant "Trang\ntĩnh" as site

dev -> file : Mở portfolio/projet.adoc
activate file

dev -> file : Chỉnh sửa thuộc tính\n:jbake-status:đã xuất bản\n↓\n:jbake-status:nháp

file --> dev : Đã được sao lưu
deactivate file

dev -> jbake : Xây dựng lại (jbake -b)
activate jbake
jbake -> file : Phân tích tệp tin
jbake -> jbake : Phát hiện status=draft
jbake -> jbake : Bỏ qua published_projects
jbake -> jbake : Đừng tạo trang công khai
note right
Dự án vẫn tồn tại
nhưng chưa được công bố
end note
jbake -> site : Tạo site (không có dự án này)
deactivate jbake

dev -> site : Kiểm tra portfolio
activate site
site --> dev : Dự án không có trong danh sách công cộng
deactivate site
@enduml

Các trạng thái có thể có

@startuml
skinparam backgroundColor #FEFEFE

[*] --> Draft : Création initiale
Draft --> Published : Xác thực và công bố
Published --> Draft : Rút tạm thời
Draft --> Archived : Dự án đã bị bỏ rơi
Published --> Archived : Dự án lỗi thời
Archived --> [*] : Xóa vĩnh viễn
Published --> Published : Cập nhật

note right of Draft
:jbake-status: nháp
Không hiển thị công khai
Hữu ích cho các dự án đang diễn ra
end note

note right of Published
:jbake-status: published
Hiển thị trên danh mục
Được lập chỉ mục bằng JBake
end note

note right of Archived
Đã di chuyển vào archive/
hoặc jbake-status: đã lưu trữ
Chưa xuất bản nhưng vẫn được giữ
end note
@enduml

Các trường hợp sử dụng điển hình

  • Dự án đang trong quá trình soạn thảo: tạo bản nháp, xuất bản khi sẵn sàng

  • Dự án tạm thời bảo mật: chuyển sang bản nháp trong thời gian chờ khách hàng đồng ý

  • Cập nhật lớn: chuyển thành bản nháp, chỉnh sửa, xuất bản lại

  • A/B testing: nhân bản thành bản nháp, thử nghiệm, xuất bản phiên bản tốt nhất

Kiến trúc Templating

Chiến lược template có thể tái sử dụng

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "Templates Thymeleaf" {
  component [index.html] as index
  component [portfolio.html] as portfolio
  component [project.html] as project

  package "Partials" {
    component [header.html] as header
    component [footer.html] as footer
    component [project-card.html] as card
    component [tech-badge.html] as badge
    component [gallery.html] as gallery
  }
}

package "dữ liệu JBake" {
  database "published_projects" as data
  database "content (dự án)" as content
}

index --> header
index --> footer

portfolio --> header
portfolio --> footer
portfolio --> card
data --> portfolio : Sự lặp lại

project --> header
project --> footer
project --> badge
project --> gallery
content --> project : dự án cá nhân

note right of card
Mảnh có thể tái sử dụng
để hiển thị một thẻ
dự án với :
- ảnh thu nhỏ
- tiêu đề
- thẻ
- những điểm nổi bật
end note
@enduml

Mẫu trích xuất dữ liệu

JBake tự động chuyển đổi các thuộc tính AsciiDoc thành các property có thể truy cập được trong Thymeleaf:

@startuml
skinparam backgroundColor #FEFEFE

rectangle "Tài liệu AsciiDoc" {
  (":project-client: Acme Corp") as attr1
  (":project-duration: 6 tháng") as attr2
  (":project-tags: java, react") as attr3
}

rectangle "Mẫu JBake" {
  (content['project-client']) as prop1
  (content['project-duration']) as prop2
  (content.tags) as prop3
}

rectangle "Mẫu Thymeleaf" {
  (th:text="${project['project-client']}") as tmpl1
  (th:text="${project['project-duration']}") as tmpl2
  (th:each="tag : ${project.tags}") as tmpl3
}

attr1 --> prop1 : parsing
attr2 --> prop2 : parsing
attr3 --> prop3 : parsing

prop1 --> tmpl1 : binding
prop2 --> tmpl2 : binding
prop3 --> tmpl3 : binding
@enduml

Quy ước đặt tên

  • Thuộc tính dự án : tiền tố`project-*` (ex: project-client, project-tech-stack)

  • Tệp tin : kebab-case (ví dụ:`ecommerce-platform.adoc`)

  • Hình ảnh : tiền tố tên-dự-án (ví dụ:`ecommerce-platform-thumb.jpg`)

  • Templates : tên chức năng (ví dụ:`project-card.html`, tech-badge.html)

Quy trình xuất bản

Luồng phát triển

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

class ProjetAsciiDoc {
  +titre: String
  +jbake-type: "dự án"
  +jbake-status: published|draft
  +jbake-date: Date
  +jbake-tags: List<String>
  __
  +project-category: String
  +project-client: String
  +project-duration: String
  +project-role: String
  +project-thumbnail: String
  +project-gallery: List<String>
  +project-tech-stack: String
  +project-highlights: List<String>
  +project-demo-url: String
  +project-github-url: String
  __
  +body: HTML (converti)
}

class JBakeEngine {
  +parse(asciidoc)
  +extractMetadata()
  +convertToHTML()
}

class ThymeleafTemplate {
  +portfolio.html
  +project.html
  +partials/project-card.html
}

class PortfolioPage {
  +published_projects: List
  +filtres: categories
}

ProjetAsciiDoc --> JBakeEngine : phân tích
JBakeEngine --> ThymeleafTemplate : cung cấp dữ liệu
ThymeleafTemplate --> PortfolioPage : tạo ra
@enduml

Môi trường

Môi trường Cách sử dụng Trạng thái đã chấp nhận

Địa phương

Phát triển và xem trước

bản nháp, đã xuất bản

Staging

Xác thực tiền sản xuất

chỉ được xuất bản

Sản xuất

Trang công khai

chỉ được xuất bản

Khả năng mở rộng

Thêm các thuộc tính mới

Để làm phong phú mô hình dữ liệu, chỉ cần thêm các thuộc tính mới có tiền tố`project-*`Vui lòng cung cấp văn bản cần dịch.

  • project-awards: Giải thưởng và công nhận

  • project-testimonial: Trích dẫn của khách hàng

  • project-team-size: Quy mô đội ngũ

  • project-budget-range: Khoảng ngân sách

Các thuộc tính này sẽ tự động khả dụng trong các template mà không cần thay đổi công cụ JBake.

Phân loại nâng cao

@startuml
skinparam backgroundColor #FEFEFE

object Projet {
  project-category = "web"
  project-subcategory = "thương mại điện tử"
  project-industry = "bán lẻ"
  project-complexity = "cao"
}

object Taxonomie {
  categories : [web, mobile, data, devops]
  subcategories : Map<category, List>
  industries : [retail, finance, healthcare, ...]
  complexities : [low, medium, high]
}

Projet --> Taxonomie : phân loại theo

note right of Taxonomie
Cho phép lọc đa tiêu chí
trong mẫu portfolio.html
thông qua JavaScript hoặc phía máy chủ
end note
@enduml

Thực hành Tốt nhất

Tổ chức tệp tin

  • Một tệp = một dự án: tránh trộn lẫn nhiều dự án

  • Hình ảnh trong thư mục riêng:`assets/img/portfolio/nom-projet/`

  • Hệ thống đặt tên nhất quán: tạo điều kiện thuận lợi cho việc tìm kiếm và bảo trì

  • Versioning Git : theo dõi đầy đủ các thay đổi

Quản lý nội dung

  • Trạng thái bản nháp mặc định: chỉ xuất bản khi đã sẵn sàng

  • Xem lại trước khi xuất bản: phê duyệt chất lượng và bảo mật

  • Siêu dữ liệu đầy đủ: điền tất cả các trường liên quan

  • Nội dung tường thuật phong phú: không giới hạn ở siêu dữ liệu

Hiệu năng

  • Tối ưu hóa hình ảnh: nén trước khi commit

  • Phân trang nếu cần thiết: nếu >20 dự án

  • Lazy loading : hình ảnh trong thư viện được tải theo yêu cầu

  • Bộ nhớ đệm trình duyệt : các header phù hợp cho assets

Kết luận

Kiến trúc này cho phép:

  • Đơn giản trong sử dụng: thêm một dự án = tạo một tệp văn bản

  • Tính linh hoạt: có thể mở rộng thông qua các thuộc tính mới

  • Khả năng bảo trì: tách biệt nội dung/trình bày

  • Khả năng truy xuất nguồn gốc: versioning Git đầy đủ

  • Tự động hóa: khả năng build và triển khai liên tục

Việc lựa chọn AsciiDoc đảm bảo tính nhất quán với phần còn lại của trang web, đồng thời cung cấp các siêu dữ liệu phong phú cần thiết cho một portfolio chuyên nghiệp.

Bài viết liên quan