Hướng dẫn thiết kế cơ sở dữ liệu cho Google Calendar
(kb.databasedesignbook.com)- Lấy chức năng lịch của một bản sao Google Calendar làm ví dụ, bài viết cho thấy luồng thiết kế bắt đầu không phải từ việc tạo bảng mà từ việc hoàn thiện mô hình logic rồi mới chuyển sang schema SQL
- Phương pháp cốt lõi là xác định trước các anchor như User, DayEvent, TimeEvent, Timezone, sau đó kiểm chứng yêu cầu và cardinality bằng thuộc tính cùng các liên kết 1:N và M:N
- Sự kiện được chia thành sự kiện cả ngày và sự kiện theo thời gian; với sự kiện theo thời gian, hệ thống lưu thời gian cục bộ của lúc bắt đầu và kết thúc cùng Timezone tương ứng để xử lý cả khả năng thay đổi múi giờ
- Lịch lặp được biểu diễn bằng chu kỳ, khoảng cách, cách lặp theo tháng, điều kiện kết thúc và liên kết với thứ trong tuần; để render giao diện và thay đổi từng instance riêng lẻ, bài viết đặt DaySlot và TimeSlot thành các anchor riêng
- Thiết kế vật lý cuối cùng dùng nguyên tắc “mỗi anchor một bảng”, nhưng không tạo bảng cho DayOfTheWeek, nên được gói gọn thành tổng cộng 8 bảng SQL gồm 6 bảng anchor và 2 bảng liên kết M:N
Cách tiếp cận thiết kế: tạo mô hình logic trước
- Mục tiêu là minh họa quy trình thiết kế bảng cơ sở dữ liệu gần với một dự án thực tế, với đối tượng là chức năng lịch của một bản sao Google Calendar
- Toàn bộ quy trình là trước tiên tạo một mô hình logic hoàn chỉnh mô tả dữ liệu lịch cần lưu, rồi chuyển mô hình đó sang cấu trúc bảng vật lý
- Phần đầu nói về mô hình logic không gắn với một hệ quản trị cơ sở dữ liệu cụ thể
- Mô hình hóa yêu cầu nghiệp vụ độc lập với MySQL, Postgres, máy chủ quan hệ truyền thống, NoSQL hay cơ sở dữ liệu đám mây
- Phần sau trình bày một cách tiếp cận để chuyển mô hình logic thành các bảng vật lý
- Với những hệ thống không có yêu cầu quá khắt khe, đây có thể được xem là một cách thiết kế hợp lý
Phạm vi bài toán: các chức năng lịch cốt lõi của Google Calendar
- Hệ thống mục tiêu là hệ thống đa người dùng, và dữ liệu liên quan đến người dùng chỉ được mô hình hóa ở mức tối thiểu
- Sự kiện có thể có các thuộc tính như tiêu đề, mô tả, địa điểm, nhưng phần phức tạp nhất là thời gian và ngày tháng
- Các sự kiện được mô hình hóa bao gồm những khác biệt sau
- Sự kiện cả ngày và sự kiện theo thời gian
- Sự kiện lặp và không lặp
- Sự kiện cả ngày kéo dài qua nhiều ngày
- Sự kiện theo thời gian có gắn múi giờ, trong đó thời điểm bắt đầu và kết thúc có thể rơi vào các ngày khác nhau hoặc các múi giờ khác nhau
- Sự kiện lặp gồm các dạng sau
- Lặp hằng ngày hoặc mỗi N ngày
- Lặp hằng tuần vào các thứ nhất định, và cũng có thể cách 2 tuần hoặc hơn
- Lặp hằng tháng vào một ngày cụ thể hoặc vào một thứ thuộc tuần cụ thể trong tháng
- Lặp hằng năm
- Lặp mãi mãi, đến một ngày cụ thể, hoặc chỉ một số lần nhất định
- Một instance cụ thể của sự kiện lặp có thể được dời sang ngày/giờ khác hoặc bị xóa
- Cũng cần có khả năng chỉnh sửa chính lịch lặp ngay cả khi một phần sự kiện đã diễn ra trong quá khứ
Biểu diễn yêu cầu bằng anchor, thuộc tính và liên kết
- Việc mô hình hóa bắt đầu bằng cách xác định anchor
- Anchor là đối tượng có thể đếm được và có thể được thêm vào cơ sở dữ liệu, như User hay Event
- Nếu có thể nói tự nhiên như “There are 200 Users in our database” hoặc “When this button is clicked, a new DayEvent is created” thì có thể xem đó là anchor
- Thuộc tính lưu thông tin thực tế về anchor
- User có thuộc tính email
- DayEvent có tên, ngày bắt đầu và ngày kết thúc
- Ngay cả sự kiện một ngày cũng lưu ngày bắt đầu và ngày kết thúc giống nhau để xử lý cùng cấu trúc với sự kiện nhiều ngày
- Khi hai anchor có quan hệ với nhau, dùng liên kết thay vì thuộc tính
- “User nào đã tạo DayEvent này” không phải là thuộc tính logic chứa trực tiếp User ID trong DayEvent, mà là liên kết giữa User và DayEvent
- Liên kết dùng cardinality 1:N, M:N hoặc 1:1, và được kiểm chứng ý nghĩa hai chiều bằng hai câu mô tả
- Giữa User và DayEvent có liên kết 1:N
- Một User có thể tạo nhiều DayEvent
- Một DayEvent được tạo bởi một User
Sự kiện theo thời gian và xử lý Timezone
- Để hỗ trợ sự kiện theo thời gian, bài viết bổ sung các anchor Timezone và TimeEvent
- Mô hình Timezone được tối giản cho mục đích hướng dẫn, chỉ có thuộc tính tên dễ đọc cho con người
- Giá trị ví dụ là
Europe/Kyiv - Trong thực tế, định nghĩa múi giờ còn cần UTC offset, có DST hay không, thời điểm DST bắt đầu/kết thúc, UTC offset khi áp dụng DST, lịch sử định nghĩa, trạng thái hoạt động/ngừng dùng, v.v., nhưng phần đó được để lại như một bài toán riêng
- Giá trị ví dụ là
- TimeEvent có tên, thời gian cục bộ ngày-giờ bắt đầu và thời gian cục bộ ngày-giờ kết thúc
- Giá trị ví dụ là
2024-01-14 12:30,2024-01-14 13:15
- Giá trị ví dụ là
- Thời gian không chỉ được lưu dưới dạng UTC mà được lưu theo đúng thời gian cục bộ người dùng nhập vào
- Ví dụ, một trận bi-a ở Cologne từ 09:30 đến 11:00 ngày 6/9/2058 không thể biết trước UTC offset tại thời điểm đó, nên phải lưu đúng như người dùng nhập
- Nếu luật địa phương thay đổi về sau, hệ thống phải có khả năng điều chỉnh theo đó
- TimeEvent có liên kết 1:N với Timezone riêng cho múi giờ bắt đầu và múi giờ kết thúc
- Như ví dụ vé máy bay, chuyến bay từ Amsterdam đến London có thể có thời gian khởi hành và đến nơi thuộc các múi giờ khác nhau
- Phần lớn sự kiện theo thời gian sẽ có cùng múi giờ ở lúc bắt đầu và kết thúc, nhưng để tổng quát thì cả hai luôn được chỉ định
Mô hình hóa sự kiện cả ngày lặp
- Sự kiện cả ngày lặp được biểu diễn bằng cách thêm các thuộc tính và liên kết liên quan đến lặp vào DayEvent
- Tần suất lặp được đặt thành thuộc tính kiểu either/or/or
- Các giá trị có thể là daily, weekly, monthly, annually
- Nếu không có giá trị thì xem là sự kiện không lặp
- Khoảng lặp là thuộc tính số nguyên chỉ có ý nghĩa “khi là sự kiện lặp”
- Giá trị ví dụ là
2 - Dùng với ý nghĩa mỗi 2 ngày, mỗi 2 tuần, mỗi 2 tháng
- Giá trị ví dụ là
- Lặp theo tháng có thêm một thuộc tính lựa chọn riêng
same_dayđể lặp vào cùng ngày trong thángsame_weekdayđể lặp vào cùng thứ trong cùng tuần như ngày gốc
- Các thứ cụ thể trong tuần đối với lặp hằng tuần không được xử lý bằng thuộc tính mảng chuỗi mà được mô hình hóa bằng anchor DayOfTheWeek và liên kết M:N
- Ví dụ ID của DayOfTheWeek là
Mon,Tue,Wed,Thu,Fri,Sat,Sun - Một DayEvent có thể xảy ra vào nhiều DayOfTheWeek, và một DayOfTheWeek có thể thuộc nhiều DayEvent
- Ví dụ ID của DayOfTheWeek là
- Điều kiện kết thúc lặp cũng là một thuộc tính
- forever
- until_date
- N_repetitions
- Nếu là until_date thì thêm thuộc tính ngày kết thúc
- Nếu là N_repetitions thì thêm thuộc tính số lần lặp
Slot để render và chỉnh sửa từng instance riêng lẻ
- Nếu chỉ lưu sự kiện lặp bằng một bản ghi sự kiện gốc, truy vấn tìm các sự kiện cần hiển thị cho một màn hình tuần cụ thể có thể trở nên phức tạp
- Để render giao diện và cho phép chỉnh sửa từng instance của sự kiện lặp, bài viết đưa vào khái niệm Slot
- DaySlot biểu diễn một instance sự kiện cả ngày tại một ngày cụ thể
- TimeSlot biểu diễn một instance sự kiện theo thời gian tại ngày-giờ cụ thể
- DaySlot có thuộc tính ngày và trạng thái bị bỏ qua
- Có thể thay đổi ngày của một slot cụ thể
- Nếu hủy cuộc họp của một tuần nào đó, DaySlot tương ứng có thể được đánh dấu skipped
- Giữa DayEvent và DaySlot có liên kết 1:N
- Một DayEvent có thể tạo ra nhiều DaySlot
- Một DaySlot tương ứng với một DayEvent
- Ngay cả DayEvent không lặp cũng luôn tạo DaySlot để đơn giản hóa mã render
- Với sự kiện lặp vô hạn, có vấn đề về phạm vi tạo slot
- Một sự kiện như sinh nhật lặp hằng năm mãi mãi có thể được tạo slot tới một giới hạn tùy ý như 100 năm
- Hoặc có thể tạo theo yêu cầu khi người dùng mở trang lịch ở tương lai xa
- Cần cẩn thận với tính toán ngày tháng
- Sinh nhật ngày 29/2
- Sự kiện hằng tháng diễn ra vào ngày 31
- Trong các trường hợp như vậy cần quyết định có cấm hay không, hoặc dời lên trước hay lùi xuống một ngày
TimeSlot và múi giờ
- Với sự kiện theo thời gian lặp, bài viết áp dụng cách tiếp cận tương tự như sự kiện cả ngày và đặt thêm anchor TimeSlot
- TimeSlot tương ứng với một instance ngày-giờ cụ thể của một sự kiện; một TimeEvent lặp có thể tương ứng với nhiều TimeSlot
- Thuộc tính của TimeSlot là thời gian cục bộ ngày-giờ bắt đầu, thời gian cục bộ ngày-giờ kết thúc và trạng thái bị bỏ qua
- Một TimeSlot cụ thể cũng có thể được dời sang ngày khác, nên cần giữ riêng thông tin thời điểm bắt đầu và kết thúc
- TimeSlot cũng có liên kết 1:N với Timezone riêng cho múi giờ bắt đầu và múi giờ kết thúc
- Giữa TimeEvent và TimeSlot có liên kết 1:N, và ngay cả TimeEvent không lặp cũng tạo TimeSlot
Mô hình logic hoàn chỉnh
- Tổng cộng có 7 anchor đã được xác định đến lúc này
- User
- Timezone
- DayEvent
- TimeEvent
- DayOfTheWeek
- DaySlot
- TimeSlot
- Các thuộc tính gồm email của User, tên của Timezone, tên/ngày bắt đầu/ngày kết thúc/thông tin lặp của DayEvent, thời gian cục bộ bắt đầu/kết thúc của TimeEvent, cùng thời điểm diễn ra và trạng thái skipped của DaySlot và TimeSlot
- Có tổng cộng 10 liên kết
- User và DayEvent, User và TimeEvent
- Timezone với múi giờ bắt đầu/kết thúc của TimeEvent
- DayEvent và DayOfTheWeek, TimeEvent và DayOfTheWeek
- DayEvent và DaySlot, TimeEvent và TimeSlot
- Timezone với múi giờ bắt đầu/kết thúc của TimeSlot
Chuyển sang các bảng SQL
- Thiết kế vật lý dùng chiến lược mỗi anchor một bảng cho mục đích giảng giải
- Mô hình logic có 7 anchor, 21 thuộc tính và 10 liên kết
- Thông thường có thể thành 9 bảng bằng cách lấy số anchor cộng số liên kết M:N, nhưng trong thiết kế cuối cùng DayOfTheWeek được xử lý đặc biệt nên không tạo bảng vật lý riêng
- Tổng số bảng SQL cuối cùng là 8
userstimezonesday_eventstime_eventsday_slotstime_slotsday_event_dowstime_event_dows
- Liên kết 1:N được đưa vào bảng phía N dưới dạng cột mang tính khóa ngoại
day_events.user_idtime_events.user_idtime_events.start_timezone_idtime_events.end_timezone_idday_slots.day_event_idtime_slots.time_event_idtime_slots.start_timezone_idtime_slots.end_timezone_id
- Liên kết M:N được tách thành bảng riêng
day_event_dowsliên kết DayEvent với thứ trong tuầntime_event_dowsliên kết TimeEvent với thứ trong tuần
- Kiểu dữ liệu vật lý được chọn tương ứng với kiểu logic
- Chuỗi dùng
VARCHAR - Ngày dùng
DATE - Ngày-giờ cục bộ dùng
DATETIME - yes/no dùng
TINYINT UNSIGNED - Các thuộc tính chỉ có ý nghĩa trong điều kiện nhất định dùng kiểu cho phép
NULL
- Chuỗi dùng
NULLvà sentinel value được xem là khái niệm của schema vật lý, không phải của mô hình logic
Chỉ mục và các thuộc tính bị lược bỏ
- Schema SQL ví dụ không bao gồm các chỉ mục mà một nhà phát triển cơ sở dữ liệu giàu kinh nghiệm có thể cho là cần thiết
- Ví dụ,
day_events.user_idcó thể cần được đánh chỉ mục
- Ví dụ,
- Việc đặt chỉ mục cho cột nào hoặc tổ hợp cột nào phụ thuộc vào cách ứng dụng truy vấn các bảng
- Với chủ đề chỉ mục, có thể tham khảo Use The Index, Luke
- Trong lúc giải thích schema logic, một số thuộc tính được lược bỏ vì theo cùng khuôn mẫu tương tự
- Tên người dùng
- Hash mật khẩu người dùng
- Địa điểm sự kiện
- Danh sách người được mời
- Các phần dữ liệu bị lược bỏ này có thể được thêm vào bảng mô hình logic theo cùng cách rồi phản ánh sang schema SQL
Toàn bộ quy trình
- Bắt đầu từ văn bản tự do mô tả bài toán nghiệp vụ
- Lập danh sách anchor
- Lập danh sách thuộc tính, đồng thời ghi rõ ý nghĩa thuộc tính dưới dạng câu hỏi
- Lập danh sách liên kết và kiểm tra cardinality bằng câu mô tả
- Nếu cần thì tạo schema đồ họa dựa trên mô hình logic
- Điền tên bảng, tên cột và kiểu dữ liệu vật lý để tạo mô hình vật lý
- Dùng thông tin từ các bước trước để viết schema SQL bằng câu lệnh
CREATE TABLE - Gửi schema lên máy chủ cơ sở dữ liệu, sửa lỗi chính tả rồi gửi lại
- Chia sẻ mô hình logic với cả nhóm
1 bình luận
Các ý kiến trên Hacker News
Nếu tuần tự hóa một sự kiện bất kỳ trong lịch của tôi sang iCalendar thì chỉ khoảng 740 byte; ngay cả một lịch cực kỳ bận rộn với sự kiện mỗi 15 phút từ 9 giờ sáng đến 5 giờ chiều thì cả năm cũng chỉ có 11.680 mục, khoảng 8MB mà thôi
Vì vậy, thay vì thiết kế schema, lịch trông giống một bài toán có thể triển khai tốt hơn nhiều bằng cách quét tuần tự. Nếu tối ưu parser iCalendar để lướt qua phạm vi sự kiện đã dump với thông lượng cỡ GB/s, thì ngay cả ca xấu nhất ở trên cũng có thể quét trong vài mili-giây một chữ số
Tối ưu parser là một bài toán đơn giản hơn nhiều, chỉ cần giải một lần, so với việc thay đổi hoặc mở rộng một mô hình dữ liệu tệ sau khi nó đã lan tới nhiều người dùng; dù sao thì việc đầu tiên của mô hình dữ liệu mới rất có thể cũng là import/export iCalendar, nên đây có vẻ là một thỏa hiệp khá tốt
BETWEEN ... AND ...của SQL sao? Việc tìm sự kiện của người dùng 1, 2, 3 cũng tương tựNếu là ứng dụng thực tế thì có lẽ đã có những thứ như tài khoản người dùng trong cơ sở dữ liệu quan hệ rồi, và đột nhiên ta phải quét các sự kiện trong thư mục rồi liên kết kết quả đó với các bản ghi trong cơ sở dữ liệu
Với một ứng dụng cụ thể thì có thể đúng, nhưng có những việc nhất định mà cơ sở dữ liệu làm thực sự rất tốt, và vì thế nó là lựa chọn tốt. Nếu có index phù hợp thì cũng có thể đạt thông lượng ngang bằng hoặc tốt hơn. Trừ khi bạn tạo một cấu trúc thư mục thông minh cho sự kiện, nhưng thực chất đó giống một index một chiều; còn trong cơ sở dữ liệu thì có thể tạo index cho nhiều chiều và các tổ hợp chiều
Rốt cuộc thì nói vậy cũng đúng, và đây là vấn đề thỏa hiệp
Tuổi đời của định dạng này thể hiện khá rõ: có thể thấy nó được thiết kế trước khi XML/JSON “lên ngôi”
Tham khảo: https://en.wikipedia.org/wiki/ICalendar
Đây là bài viết về việc xử lý lịch lặp lại trong ứng dụng, và cá nhân tôi thấy rất mở mang. Rất đáng đọc: https://github.com/bmoeskau/Extensible/blob/master/recurrenc...
Bài gốc nói luôn lưu múi giờ cho mỗi ngày, còn bài được liên kết nói hãy chuyển tất cả sang UTC. Ở điểm này tôi đồng ý với bài gốc
Bài gốc nói tạo một hàng trong cơ sở dữ liệu cho mỗi sự kiện, còn bài được liên kết nói đừng làm vậy. Ở điểm này tôi đồng ý với bài được liên kết
Lần tôi thất bại nặng nhất trong phỏng vấn là khi phải xây một hệ thống đặt chỗ đơn giản nhưng còn phải xử lý cả đặt chỗ lặp lại
Từ sau những năm đầu đại học, tôi chưa từng cảm thấy lạc lối và rối trí đến vậy khi cố làm một thứ gì đó bằng code. Đến giờ hễ phải làm việc liên quan đến ngày/giờ là tôi đã thấy sợ trước; không hiểu vì sao nhưng trong đầu tôi mọi thứ cứ không khớp với nhau
Nhìn bề ngoài thì đơn giản, nhưng nếu muốn nó hoạt động tổng quát trong thế giới thực thì vấn đề trở nên phức tạp đến mức gần như bất khả thi
Đây là một bài toán khó một cách đánh lừa, và đòi hỏi kỹ năng mô hình hóa dữ liệu rất tốt
Khả năng mô hình hóa hệ thống đang bị đánh giá thấp. Trong một miền mới, cần bắt đầu bằng phân tích toàn bộ vấn đề để nắm được cả cấu trúc tĩnh của hệ thống, tức mô hình lớp, lẫn hành vi động, tức các ca sử dụng
Nếu nhảy ngay vào mô hình cơ sở dữ liệu tĩnh thì rất dễ bỏ sót hành vi động. Với các ứng dụng CRUD đơn giản như thế này có thể vẫn ổn, nhưng trong các hệ thống phức tạp hơn thì đó có thể là sai lầm lớn
Câu cuối tôi nhớ là sẽ thiết kế bảng sản phẩm có coupon như thế nào. Ban đầu tôi nghĩ chắc hai bảng là đủ, nhưng rồi có cần lưu lịch sử không? Nếu vậy phải đưa ngày/giờ vào theo từng sản phẩm và coupon rồi cập nhật. Giờ lại phải nghĩ đến index của bảng, rồi liệu join để lấy giá sau giảm có phải cách hay không. Phần lớn coupon mỗi người chỉ dùng được một lần, vậy rốt cuộc triển khai điều đó thế nào?
Có lẽ họ chỉ muốn một bảng sản phẩm + coupon đơn giản, nhưng một mình tôi lại đào sâu điên cuồng một hồi lâu
Tôi thấy đây là một miền được chọn tốt để minh họa mô hình hóa, và là một bài nhập môn hay
Thuật ngữ “anchor” nghe hơi lạ, nhưng phần giải thích rất cụ thể và “bám đất” như một cái neo thật sự, nên có vẻ vẫn ổn
Khái niệm định nghĩa thuộc tính bằng câu hỏi rất vững. Chúng ta quá thường lao ngay vào một tập tên cột/thuộc tính tối thiểu mà chưa định nghĩa mình đang cố trả lời những câu hỏi nào; làm vậy sẽ không xua tan được sự mơ hồ trong đầu khách hàng. Định nghĩa thuộc tính dựa trên câu hỏi là một cách tốt để nhanh chóng đạt được sự rõ ràng
Hơn nữa thuật ngữ này bị quá tải nghiêm trọng. Trong khoa học máy tính, gần như mọi thứ đều là object hoặc entity
Phản hồi rằng khái niệm định nghĩa thuộc tính bằng câu hỏi là vững chắc là một xác nhận quan trọng về tính đúng đắn của cách tiếp cận này
Múi giờ đặc biệt dễ làm rối não quanh các thời điểm chuyển đổi
Hãy giả sử múi giờ nhảy tiến một giờ khi vào giờ tiết kiệm ánh sáng ban ngày, và lùi lại một giờ khi quay về giờ chuẩn
Khi thời gian nhảy tiến một giờ, một sự kiện kéo dài 1 giờ có thể trông như kéo dài 2 giờ trên màn hình, và giờ thứ hai đó có thể không thể tới được hoặc không tồn tại
Khi thời gian lùi lại một giờ, một sự kiện kéo dài 1 giờ có thể trông như 2 giờ, hoặc như 0 giờ
Múi giờ là cấu trúc do con người tạo ra, nên không được hard-code giá trị. Một ngày nào đó chúng sẽ thay đổi
Bạn có thể liên tục qua lại giữa các múi giờ theo cả hai hướng, có thể băng qua đường đổi ngày quốc tế, và cũng có thể xảy ra trường hợp ngày/giờ bắt đầu theo giờ địa phương lại muộn hơn ngày/giờ kết thúc theo giờ địa phương
Thà tập trung loại bỏ khái niệm giờ tiết kiệm ánh sáng ban ngày ở một số khu vực vẫn còn dùng nó thì hơn. Nó gây ra nhiều nhầm lẫn nhất giữa các múi giờ, đặc biệt khi lên lịch cho các thời điểm sau lần chuyển đổi sắp tới
Tôi không muốn lưu hai ngày cho một sự kiện. Lưu thời điểm bắt đầu và thời lượng của sự kiện thì dễ hơn
Làm như vậy logic cập nhật sự kiện sẽ đơn giản hơn. Thời điểm kết thúc luôn có thể được tính dựa trên thời điểm bắt đầu và thời lượng
Trước đây tôi từng làm một ứng dụng lịch có lịch lặp. Sau nhiều nghiên cứu, chúng tôi quyết định dùng RRules để biểu diễn việc này, và tôi rất hài lòng. Phần công việc ban đầu đó được thực hiện khi tôi còn ở agency
Sau này khi gia nhập công ty đó với tư cách nhân viên chính thức, tôi đã sốc khi thấy một nhà thầu từ công ty khác đã gỡ RRules và chuyển sang cách tạo rồi xóa các instance sự kiện tại chỗ. Khả năng chịu lỗi gần như không có, nên một script thỉnh thoảng thất bại trong lúc còn làm các việc khác cũng sẽ không tạo được sự kiện mới. Thế là trong một sự kiện lặp hằng tháng sẽ có tháng bị thiếu
Thật bực bội khi thấy thứ được tạo ra sau rất nhiều suy nghĩ và nghiên cứu lại bị ai đó loại bỏ mà không bỏ ra mức công sức tương xứng. Chỉ ở công ty đó vài tuần là có thể thấy CEO kỳ vọng đội engineering cứ sản xuất liên tục các tính năng không ai dùng theo ý mình; và đáng buồn là vì thị trường tuyển dụng bất định, tôi đã ở lại 2 năm
Nhân tiện, sau này tôi tìm thử thì thấy trên Glassdoor có các review giả rõ ràng do CEO viết, tất cả cùng một văn phong và không có lấy một lời chê, thật sự rất chua chát. Tôi và nhiều người quen từng làm ở đó đều ghét ông ta, nhưng ít ra việc tôi đã viết vài bài luận hay nhất của mình ở công ty đó cũng là một chút an ủi. CTO cũng chẳng khá hơn
Có thời tôi từng triển khai backend lịch và kiểm soát tài nguyên cho một nền tảng low-code
Control có khả năng tùy biến rất mạnh, có thể chọn nhiều kiểu xem như ngày, tháng, năm, cũng như cả chế độ xem tài nguyên. Có thể đặt trước tài nguyên theo nhóm tùy chỉnh, và nhóm theo plugin, theo ID tài nguyên, v.v. Chúng tôi định nghĩa “plugins” trong nguồn dữ liệu, đồng thời chỉ định cột bắt đầu/kết thúc, cột tiêu đề và cột tài nguyên. Tài nguyên có thể đến từ khóa ngoại hoặc quan hệ 1:1, cũng có thể đến từ quan hệ 1:N của nguồn dữ liệu “con” hoặc từ cùng nguồn dữ liệu/bảng
Chúng tôi cũng triển khai nhiều loại chuỗi cuộc hẹn như hằng tháng, hằng tuần, vào ngày cụ thể trong tuần, hằng ngày, v.v., và cho phép chọn những giá trị cột nào sẽ được sao chép. Chúng tôi cũng xử lý xung đột cuộc hẹn, trong đó có thể chỉ coi là xung đột khi đặt cùng một tài nguyên. Cũng có thể cấu hình buffer trước và sau cuộc hẹn, trong khoảng đó không thể chèn cuộc hẹn khác
Đôi khi khá thử thách vì múi giờ ở châu Âu và giờ mùa hè/mùa đông, nhưng đó là một công việc khá thú vị
Điều tôi muốn ở Google Calendar chỉ là nhật ký thay đổi của chính lịch. Làm ơn thêm cái này vào cơ sở dữ liệu đi