วิธีการลบการสืบทอดตารางเดี่ยวจากรางของคุณ Monolith

การสืบทอดนั้นง่าย - จนกว่าคุณจะต้องจัดการกับหนี้และภาษีทางเทคนิค

เมื่อ codebase หลักของ Learn เกิดขึ้นเมื่อห้าปีที่แล้ว Single Table Inheritance (STI) นั้นค่อนข้างได้รับความนิยม ทีมงาน Flatiron Labs ในเวลานั้นใช้มันทุกอย่างตั้งแต่การประเมินและหลักสูตรไปจนถึงกิจกรรมฟีดข้อมูลและเนื้อหาภายในระบบการจัดการการเรียนรู้ที่เพิ่มขึ้นของเรา และนั่นก็ยอดเยี่ยม - มันทำงานเสร็จแล้ว อนุญาตให้ผู้สอนส่งมอบหลักสูตรติดตามความก้าวหน้าของนักเรียนและสร้างประสบการณ์ผู้ใช้ที่น่าดึงดูด

แต่โพสต์บล็อกจำนวนมากได้ชี้ให้เห็น (อันนี้, อันนี้, และอันนี้ยกตัวอย่าง), STI ไม่ได้ปรับขนาดได้ดีโดยเฉพาะอย่างยิ่งเมื่อข้อมูลเติบโตขึ้นและ subclasses ใหม่เริ่มแตกต่างกันอย่างมากจากซูเปอร์คลาสและกันและกัน ในขณะที่คุณอาจเดาได้เกิดขึ้นใน codebase ของเรา! โรงเรียนของเราขยายออกไปและเราสนับสนุนฟีเจอร์และประเภทบทเรียนต่างๆ เมื่อเวลาผ่านไปนางแบบเริ่มขยายตัวและกลายพันธุ์และไม่สะท้อนสิ่งที่เป็นนามธรรมที่เหมาะสมสำหรับโดเมนอีกต่อไป

เราอาศัยอยู่ในพื้นที่นั้นชั่วครู่หนึ่งโดยให้รหัสนั้นกว้างและแก้ไขเมื่อจำเป็นเท่านั้น จากนั้นเวลาก็มาถึง refactor

ในช่วงไม่กี่เดือนที่ผ่านมาฉันเริ่มภารกิจเพื่อลบหนึ่งตัวอย่างที่น่ากลัวของ STI ซึ่งเกี่ยวข้องกับแบบจำลองเนื้อหาชื่อค่อนข้างคลุมเครือ เป็นเรื่องง่ายเหมือน STI ในการตั้งค่าเริ่มต้นมันเป็นเรื่องยากที่จะลบ

ดังนั้นในโพสต์นี้ฉันจะพูดถึง STI เล็กน้อยให้บริบทเกี่ยวกับโดเมนของเราร่างขอบเขตของงานและหารือเกี่ยวกับกลยุทธ์ที่ฉันใช้เพื่อปรับใช้การเปลี่ยนแปลงอย่างปลอดภัยในขณะที่ลดพื้นที่ผิวสำหรับความเสียหายร้ายแรงในขณะที่ฉันเสียใจ ของแอพของเรา

เกี่ยวกับการสืบทอด Single Table (STI)

กล่าวโดยย่อ Single Table Inheritance ใน Rails ช่วยให้คุณสามารถเก็บคลาสได้หลายประเภทในตารางเดียวกัน ใน Active Record ชื่อคลาสจะถูกเก็บไว้เป็นประเภทในตาราง ตัวอย่างเช่นคุณอาจมี Lab, Readme และ Project ทั้งหมดอยู่ในตารางเนื้อหา:

ห้องปฏิบัติการ <เนื้อหา; ปลาย
ระดับ Readme <เนื้อหา; ปลาย
โครงการระดับ <เนื้อหา; ปลาย

ในตัวอย่างนี้แล็บ readmes และโครงงานเป็นเนื้อหาทุกประเภทที่อาจเกี่ยวข้องกับบทเรียน

สคีมาของตารางเนื้อหาของเรามีลักษณะเช่นนี้ดังนั้นคุณจะเห็นได้ว่าประเภทนั้นเพิ่งถูกจัดเก็บไว้ในตาราง

create_table "content", force:: cascade do | t |
  t.integer "หลักสูตร_id",
  t.string "type"
  t.text "markdown_format",
  t.string "ชื่อ"
  t.integer "track_id"
  t.integer "github_repository_id"
ปลาย

ระบุขอบเขตของงาน

เนื้อหาแผ่ขยายไปทั่วแอพบางครั้งก็สับสน ตัวอย่างเช่นสิ่งนี้อธิบายความสัมพันธ์ในโมเดลบทเรียน

บทเรียนในชั้นเรียน <หลักสูตร
  has_many: content, -> {order (ลำดับ:: asc)}
  has_one: เนื้อหา, foreign_key:: หลักสูตร _id
  has_many: readmes, foreign_key:: หลักสูตร _id
  has_one: ห้องปฏิบัติการ, foreign_key:: หลักสูตร _id
  has_one: readme, foreign_key:: หลักสูตร _id
  has_many: assign_repos, ถึง:: content
ปลาย

สับสน? เช่นเดียวกับฉันและนั่นเป็นเพียงรูปแบบเดียวของหลาย ๆ อย่างที่ฉันต้องเปลี่ยน

ดังนั้นด้วยเพื่อนร่วมทีมที่ยอดเยี่ยมและมีความสามารถของฉัน (Kate Travers, Steven Nunez และ Spencer Rogers) ฉันได้ระดมสมองการออกแบบที่ดีขึ้นเพื่อช่วยลดความสับสนและทำให้ระบบนี้ขยายได้ง่ายขึ้น

การออกแบบใหม่

แนวคิดที่เนื้อหาพยายามแสดงเป็นตัวกลางระหว่าง GithubRepository และบทเรียน

เนื้อหาบทเรียน“ canonical” แต่ละชิ้นเชื่อมโยงกับที่เก็บใน GitHub เมื่อมีการเผยแพร่บทเรียนหรือ“ ปรับใช้” ให้กับนักเรียนเราจะทำสำเนาของที่เก็บ GitHub นั้นและให้ลิงก์แก่นักเรียน ลิงก์ระหว่างบทเรียนและเวอร์ชันที่ปรับใช้เรียกว่า AssignedRepo

ดังนั้นจึงมีที่เก็บ GitHub ทั้งสองด้านของบทเรียน: รุ่นมาตรฐานและรุ่นที่ปรับใช้

เนื้อหาคลาส 
คลาส AssignedRepo 

เมื่อถึงจุดหนึ่งบทเรียนก็สามารถมีเนื้อหาได้หลายส่วน แต่ในโลกปัจจุบันของเรานั่นไม่ใช่กรณีอีกต่อไป แต่มีบทเรียนหลายประเภทซึ่งสามารถวิปัสสนาเองได้โดยดูที่ไฟล์ที่รวมอยู่ในที่เก็บที่เกี่ยวข้อง

ดังนั้นสิ่งที่เราตัดสินใจทำคือแทนที่เนื้อหาด้วยแนวคิดใหม่ที่เรียกว่า CanonicalMaterial และให้ AssignedRepo อ้างอิงโดยตรงไปยังบทเรียนที่เกี่ยวข้องแทนการอ่านเนื้อหา

เก่าไปยังไดอะแกรมระบบใหม่โดยที่เส้นประสีแดงระบุเส้นทางที่ทำเครื่องหมายไว้สำหรับการคัดค้าน

หากฟังดูสับสนและชอบการทำงานมากมันเป็นเพราะ สิ่งสำคัญคือเราต้องแทนที่โมเดลในโค้ดเบสที่ค่อนข้างใหญ่และจบลงด้วยการเปลี่ยนแปลงบางส่วนในขอบเขตของโค้ด 6,000 บรรทัด

สิ่งสำคัญคือเราต้องแทนที่โมเดลในโค้ดเบสที่ค่อนข้างใหญ่และจบลงด้วยการเปลี่ยนแปลงบางส่วนในขอบเขตของโค้ด 6,000 บรรทัด

กลยุทธ์สำหรับการปรับโครงสร้างและเปลี่ยน STI

รูปแบบใหม่

อันดับแรกเราสร้างตารางใหม่ที่ชื่อว่า canonical_materials และสร้างโมเดลและการเชื่อมโยงใหม่

คลาส CanonicalMaterial 

นอกจากนี้เรายังเพิ่มคีย์ต่างประเทศของ canonical_material_id ลงในตารางหลักสูตรเพื่อให้บทเรียนสามารถรักษาข้อมูลอ้างอิงได้

ลงในตาราง assign_repos เราได้เพิ่มคอลัมน์ lesson_id

เขียนคู่

หลังจากมีตารางและคอลัมน์ใหม่แล้วเราเริ่มเขียนลงในตารางเก่าและใหม่พร้อมกันเพื่อที่เราจะได้ไม่ต้องเรียกใช้งานโฆษณาทดแทนมากกว่าหนึ่งครั้ง เมื่อใดก็ตามที่บางสิ่งพยายามสร้างหรืออัปเดตแถวเนื้อหาเราจะสร้างหรืออัปเดต canonical_material ด้วย

ตัวอย่างเช่น:

lesson.build_content (
  'repo_name' => repo.name
  'github_repository_id' => repo_id
  'markdown_format' => repo.readme
)

lesson.canonical_material = repo.canonical_material
lesson.save

สิ่งนี้ทำให้เราสามารถวางรากฐานสำหรับการลบเนื้อหาในที่สุด

backfilling

ขั้นตอนต่อไปในกระบวนการนี้คือการเติมข้อมูลซ้ำ เราเขียนงานเรคเพื่อเติมข้อมูลในตารางของเราและตรวจสอบให้แน่ใจว่ามี CanonicalMaterial อยู่สำหรับ GithubRepository แต่ละรายการและแต่ละบทเรียนมีวัสดุ CanonicalMaterial จากนั้นเราก็วิ่งไปที่เซิร์ฟเวอร์ที่ใช้งานจริงของเรา

ในการปรับโครงสร้างรอบนี้เราต้องการให้มีข้อมูลที่ถูกต้องเพื่อให้เราสามารถหยุดพักอย่างสะอาดตาด้วยวิธีดั้งเดิมในการทำสิ่งต่างๆ อย่างไรก็ตามตัวเลือกที่ทำงานได้อีกอย่างหนึ่งคือการเขียนโค้ดที่ยังคงรองรับรุ่นที่เก่ากว่า จากประสบการณ์ของเรามีความสับสนและค่าใช้จ่ายในการบำรุงรักษาโค้ดที่รองรับการคิดแบบดั้งเดิมมากกว่าที่จะทำการ backfill และตรวจสอบให้แน่ใจว่าข้อมูลนั้นถูกต้อง

จากประสบการณ์ของเรามีความสับสนและค่าใช้จ่ายในการบำรุงรักษาโค้ดที่รองรับการคิดแบบดั้งเดิมมากกว่าที่จะทำการ backfill และตรวจสอบให้แน่ใจว่าข้อมูลนั้นถูกต้อง

การแทนที่

จากนั้นส่วนที่สนุกก็เริ่มขึ้น เพื่อให้การแทนที่ปลอดภัยที่สุดเท่าที่จะเป็นไปได้เราใช้ฟีเจอร์แฟล็กเพื่อส่งรหัสมืดใน PRs ขนาดเล็กซึ่งช่วยให้เราสามารถสร้างลูปข้อเสนอแนะได้เร็วขึ้นและทราบได้เร็วขึ้นหากสิ่งต่าง ๆ แตกหัก เราใช้อัญมณีการเปิดตัวซึ่งเราใช้สำหรับการพัฒนาคุณสมบัติมาตรฐานเพื่อทำเช่นนี้

สิ่งที่จะค้นหา

หนึ่งในส่วนที่ยากที่สุดในการทำหน้าที่แทนคือจำนวนสิ่งที่ต้องค้นหา คำว่า "เนื้อหา" นั้นน่าเสียดายเป็นอย่างยิ่งดังนั้นจึงเป็นไปไม่ได้ที่จะทำการค้นหาทั่วโลกแบบง่าย ๆ และแทนที่ดังนั้นฉันจึงมีแนวโน้มที่จะทำการค้นหาแบบ จำกัด ขอบเขตมากขึ้นเพื่อพยายามหารูปแบบที่หลากหลาย

เมื่อลบ STI นี่คือสิ่งที่คุณควรค้นหา:

  • รูปแบบเอกพจน์และพหูพจน์ของโมเดลรวมถึงคลาสย่อยทั้งหมดวิธีเมธอดยูทิลิตี้การเชื่อมโยงและเคียวรี
  • แบบสอบถาม SQL แบบ Hardcoded
  • ตัวควบคุม
  • serializers
  • เข้าชม

ตัวอย่างเช่นสำหรับเนื้อหาที่หมายถึงการมองหา:

  • : content - สำหรับการเชื่อมโยงและการสืบค้น
  • : เนื้อหา - สำหรับการเชื่อมโยงและแบบสอบถาม
  • .joins (: content) - สำหรับการสืบค้นที่เข้าร่วมซึ่งควรถูกค้นหาโดยการค้นหาก่อนหน้า
  • .includes (: content) - สำหรับความกระตือรือร้นในการโหลดการเชื่อมโยงลำดับที่สองซึ่งควรจะถูกค้นหาโดยการค้นหาก่อนหน้า
  • เนื้อหา: - สำหรับข้อความค้นหาที่ซ้อนกัน
  • เนื้อหา: - แบบสอบถามที่ซ้อนกันอีกครั้ง
  • content_id - สำหรับการค้นหาโดยตรงโดยใช้ id
  • .content - การเรียกเมธอด
  • .contents - การเรียกเมธอดการรวบรวม
  • .build_content - วิธียูทิลิตี้ที่เพิ่มโดยการเชื่อมโยง has_one และ own_to
  • .create_content - วิธียูทิลิตี้ที่เพิ่มโดยการ has_one และเป็นของสมาคม
  • .content_ids - วิธียูทิลิตี้ที่เพิ่มโดยการเชื่อมโยง has_many
  • เนื้อหา - ชื่อคลาสเอง
  • เนื้อหา - สตริงธรรมดาสำหรับการอ้างอิงฮาร์ดโค้ดหรือแบบสอบถาม SQL

ฉันเชื่อว่าเป็นรายการที่ครอบคลุมสำหรับเนื้อหา จากนั้นฉันก็ทำเช่นเดียวกันสำหรับห้องทดลอง readme และโครงการ คุณจะเห็นได้ว่าเนื่องจาก Rails นั้นมีความยืดหยุ่นและเพิ่มวิธีการใช้งานยูทิลิตี้มากมายมันจึงยากที่จะหาสถานที่ทั้งหมดที่มีการใช้โมเดล

วิธีเปลี่ยนการใช้งานจริงหลังจากที่คุณพบผู้โทรทั้งหมด

เมื่อคุณพบไซต์โทรทั้งหมดของโมเดลที่คุณต้องการแทนที่หรือลบออกจริง ๆ แล้วคุณจะต้องเขียนสิ่งใหม่ โดยทั่วไปกระบวนการที่เราติดตามคือ

  1. แทนที่พฤติกรรมวิธีการในการกำหนดหรือเปลี่ยนวิธีการที่เว็บไซต์โทร
  2. เขียนวิธีการใหม่และเรียกพวกเขาอยู่ด้านหลังการตั้งค่าคุณสมบัติที่เว็บไซต์โทร
  3. ทำลายการพึ่งพาการเชื่อมโยงกับวิธีการ
  4. เพิ่มข้อผิดพลาดที่อยู่เบื้องหลังการตั้งค่าสถานะคุณลักษณะหากคุณไม่แน่ใจเกี่ยวกับวิธีการ
  5. สลับในวัตถุที่มีอินเทอร์เฟซเดียวกัน

นี่คือตัวอย่างของแต่ละกลยุทธ์

1a แทนที่พฤติกรรมหรือแบบสอบถามวิธีการ

บางส่วนของการเปลี่ยนค่อนข้างตรงไปตรงมา คุณวางการตั้งค่าสถานะของคุณสมบัติเพื่อพูดว่า“ เรียกรหัสนี้แทนรหัสอื่น ๆ นี้เมื่อมีการเปิดใช้สถานะนี้”

ดังนั้นแทนที่จะทำการสืบค้นตามเนื้อหาเราจะทำการค้นหาตาม canonical_material

1b เปลี่ยนวิธีการที่ไซต์การโทร

บางครั้งการเปลี่ยนวิธีการที่ไซต์การโทรเป็นเรื่องง่ายกว่าจะทำให้วิธีการที่เรียกว่ามาตรฐานเป็นมาตรฐาน (คุณควรเรียกใช้ชุดทดสอบและ / หรือเขียนการทดสอบเมื่อคุณทำเช่นนี้) การทำเช่นนั้นสามารถเปิดเส้นทางไปสู่การปรับโครงสร้างใหม่เพิ่มเติม

ตัวอย่างนี้แสดงวิธีแบ่งการพึ่งพาในคอลัมน์ canonical_id ซึ่งจะไม่มีอยู่อีกต่อไป โปรดสังเกตว่าเราเปลี่ยนวิธีการที่ไซต์การโทรโดยไม่วางสิ่งนั้นไว้เบื้องหลังการตั้งค่าสถานะ ในการทำ refactoring นี้เราสังเกตเห็นว่าเราถอน canonical_id มากกว่าหนึ่งแห่งเราจึงสรุปตรรกะเพื่อทำสิ่งนั้นในวิธีอื่นที่เราสามารถโยงไปยังแบบสอบถามอื่น ๆ วิธีการที่ไซต์การโทรเปลี่ยนไป แต่พฤติกรรมไม่ได้เปลี่ยนไปจนกว่าจะเปิดใช้งานการตั้งค่าคุณสมบัติ

2. เขียนวิธีการใหม่และเรียกพวกเขาอยู่ด้านหลังการตั้งค่าคุณสมบัติที่เว็บไซต์โทร

กลยุทธ์นี้เกี่ยวข้องกับการเปลี่ยนเมธอดซึ่งเราจะเขียนวิธีการใหม่และเรียกมันว่าอยู่เบื้องหลังการตั้งค่าสถานะที่ไซต์การโทร มันมีประโยชน์อย่างยิ่งสำหรับวิธีการที่ถูกเรียกในที่เดียวเท่านั้น นอกจากนี้ยังทำให้เราสามารถให้ลายเซ็นที่ดีขึ้นซึ่งเป็นประโยชน์เสมอ

3. ทำลายการพึ่งพาการเชื่อมโยงกับวิธีการ

ในตัวอย่างถัดไปนี้แทร็กมีห้องปฏิบัติการจำนวนมาก เนื่องจากเรารู้ว่าการเชื่อมโยง has_many เพิ่มวิธีการยูทิลิตี้เราจึงแทนที่หนึ่งบรรทัดที่มีการเรียกมากที่สุดและลบบรรทัด has_many: labs วิธีนี้เป็นไปตามอินเทอร์เฟซเดียวกันดังนั้นสิ่งใดก็ตามที่เรียกวิธีก่อนที่จะเปิดใช้คุณลักษณะนี้จะทำงานต่อไปได้

4. เพิ่มข้อผิดพลาดที่อยู่เบื้องหลังการตั้งค่าสถานะคุณลักษณะหากคุณไม่แน่ใจเกี่ยวกับวิธีการ

มีบางครั้งที่เราไม่แน่ใจว่าเราพลาดไซต์โทรหรือไม่ ดังนั้นแทนที่จะเป็นเพียงวิธีการถอดฮาร์ดไดรฟ์ในตอนแรกเราจึงแจ้งข้อผิดพลาดโดยเจตนาเพื่อให้เราสามารถตรวจจับพวกเขาได้ในระหว่างขั้นตอนการทดสอบด้วยตนเอง สิ่งนี้ทำให้เรามีวิธีที่ดีกว่าในการติดตามว่ามีการเรียกใช้วิธีการใด

5. สลับในวัตถุที่มีอินเทอร์เฟซเดียวกัน

เนื่องจากเราต้องการกำจัดสมาคมห้องปฏิบัติการเราจึงเขียนการใช้งานห้องปฏิบัติการใหม่ วิธี. แทนที่จะตรวจสอบการมีอยู่ของเรคคอร์ดแล็บเราสลับใน canonical_material มอบหมายการโทรและทำให้วัตถุนั้นตอบสนองต่อวิธีการเดียวกัน

สิ่งเหล่านี้เป็นกลยุทธ์ที่มีประโยชน์ที่สุดสำหรับการทำลายการพึ่งพาและการแลกเปลี่ยนในวัตถุใหม่ตลอดเส้นทางรถไฟโมโนลิ ธ ของเรา หลังจากตรวจสอบคำจำกัดความหลายร้อยรายการและไซต์การโทรเราได้แทนที่หรือเขียนซ้ำพวกเขาทีละรายการ เป็นกระบวนการที่น่าเบื่อที่ฉันไม่ต้องการให้ใคร แต่ท้ายที่สุดมันมีประโยชน์อย่างยิ่งในการทำให้ codebase ของเราอ่านง่ายขึ้นและเพื่อลบโค้ดเก่าที่ไม่ได้ทำอะไรเลย ต้องใช้เวลาหลายสัปดาห์ในการกำจัดขนและน่าผิดหวัง แต่เมื่อเราแทนที่ส่วนใหญ่ของการอ้างอิงแล้วเราก็เริ่มทำการทดสอบด้วยตนเอง

การทดสอบและการทดสอบด้วยตนเอง

เนื่องจากการเปลี่ยนแปลงส่งผลกระทบต่อคุณสมบัติต่างๆใน codebase ทั้งหมดซึ่งบางอย่างไม่ได้อยู่ในการทดสอบจึงยากที่จะรับประกันคุณภาพ แต่เราก็พยายามอย่างดีที่สุด เราทำการทดสอบด้วยตนเองบนเซิร์ฟเวอร์ QA ของเราซึ่งตรวจพบข้อผิดพลาดและคดีขอบจำนวนมาก จากนั้นเราก็ไปข้างหน้าและหาเส้นทางที่สำคัญยิ่งขึ้นเขียนการทดสอบใหม่

แผ่ออกใช้ชีวิตและทำความสะอาด

หลังจากผ่านการควบคุมคุณภาพเราพลิกการตั้งค่าคุณสมบัติของเราและปล่อยให้ระบบชำระ หลังจากเราแน่ใจว่ามันเสถียรแล้วเราก็ลบแฟลกของคุณสมบัติและพา ธ ของโค้ดเก่าออกจาก codebase น่าเศร้าที่นี้หนักกว่าที่คาดไว้เพราะมีการเขียนชุดทดสอบจำนวนมากซึ่งส่วนใหญ่เป็นโรงงานที่ใช้โมเดลเนื้อหาโดยปริยาย ในการหวนกลับสิ่งที่เราสามารถทำได้คือเขียนชุดการทดสอบสองชุดในขณะที่เราทำการปรับโครงสร้างอีกชุดหนึ่งสำหรับรหัสปัจจุบันและอีกชุดสำหรับรหัสที่อยู่ด้านหลังการตั้งค่าคุณลักษณะ

เป็นขั้นตอนสุดท้ายที่ยังมาไม่ถึงเราควรสำรองข้อมูลและวางตารางที่ไม่ได้ใช้

และนั่นคือเพื่อนเป็นวิธีหนึ่งที่คุณจะกำจัดการแผ่กิ่งก้านสาขาเดี่ยวในแผ่กิ่งก้านสาขาของคุณในรางเดียว บางทีกรณีศึกษานี้อาจช่วยคุณได้เช่นกัน

คุณมีวิธีอื่นในการลบ STI หรือการเปลี่ยนโครงสร้างใหม่หรือไม่? เราอยากรู้อยากเห็น แจ้งให้เราทราบในความคิดเห็น.

นอกจากนี้เรากำลังว่าจ้าง! เข้าร่วมทีมของเรา ฉันเจ๋งมากฉันสัญญา

ทรัพยากรและการอ่านเพิ่มเติม

  • คู่มือการสืบทอดทางราง
  • อย่างไรและเมื่อใดที่จะใช้การสืบทอดตารางเดี่ยวใน Rails โดย Eugene Wang (Flatiron Grad!)
  • Refactoring Rails App ของเราออกมาจากมรดกโต๊ะเดียว
  • การสืบทอดแบบตารางเดี่ยวกับความสัมพันธ์แบบ Polymorphic ใน Rails
  • การสืบทอดตารางเดี่ยวโดยใช้ราง 5.02

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับโรงเรียน Flatiron เยี่ยมชมเว็บไซต์ติดตามเราบน Facebook และ Twitter และเยี่ยมชมเราในกิจกรรมใกล้บ้านคุณ

Flatiron School เป็นสมาชิกที่น่าภาคภูมิใจของครอบครัว WeWork ตรวจสอบบล็อกเทคโนโลยีน้องสาวของเราเทคโนโลยี WeWork และการพบปะ