خانه / ٌWeb Service / Richardson Maturity Model

Richardson Maturity Model

مدل تکاملی RMM جهت توصیف ویژگی های اصلی یک سرویس RESTful و چگونگی تکامل و رسیدن از سرویس های موسوم RPC به سرویس RESTful توسط لئونارد ریچاردسون ارائه شده است. متن زیر مقاله ی آقای Martin Fowler در توضیح این مدل می باشد که به خوبی با ارئه ی یک مثال کاربردی نوبت دهی جنبه های مختلف این مدل را مورد بررسی قرار داده است.

Richardson Maturity Model

گام هایی به سمت شهرت REST

مدلی(توسط لئونارد ریچاردسون توسعه داده شده) که عناصر اصلی یک رویکرد REST رو به سه گام می شکند. این ها شامل resource ها, http verb ها و hypermedia controller ها می باشد.

اخیرا پیش نویس Rest In Practice را مطالعه کردم؛ کتابی که چند نفر از همکارانم بر روی آن کار کردند. کمک اصلی آنها توضیح اینکه به چه صورت وب سرویس های REST را جهت کنترل بسیاری از مشکلات یکپارچه سازی که سازمان ها با آنها مواجه می باشند؛ می باشد. قسمت اصلی کتاب این مفهوم است که وب یک سیستم توزیع شده ی انبوه هست که بخوبی کار می کند, و ما قادریم که جهت ایجاد ساده تر سیستم های یکپارچه شده از آن ایده بگیریم.


 Figure 1: Steps toward REST

جهت کمک به توضیح ویژگی های خاص یک سیستم وب-مانند؛ نویسنده ها یک مدل بلوغ سیستم های restful که توسط Leonard Richardson توسعه داده شده و در QCon در مورد آن صحبت شد؛ استفاده کردند.

—————————————————————————————————–

سطح صفر

نقطه ی شروع برای مدل استفاده از HTTP به عنوان سیستم انتقال در تعاملات راه دور است, اما بدون استفاده از هر کدام از مکانیزم های وب. در حقیقت چیزی که در اینجا انجام می دهید استفاده از HTTP به عنوان مکانیزم های تونلینگ برای مکانیزم های تعاملات و فراخوانی های راه دور خودتان می باشد, به طور معمول  Remote Procedure Invocation.

Figure 2: An example interaction at Level 0
POST /appointmentService HTTP/1.1فرض کنید می خواهیم نوبتی را از دکتر خودم بگیرم. نرم افزار نوبت دهی من در ابتدا نیاز داره که بدونه دکتر من برای تاریخ خاص چه نوبت هایی رو باز داره؛ بهیمن دلیل نرم افزار درخواستی رو به سیستم نوبت دهی بیمارستان جهت گرفتن اون اطلاعات ایجاد می کنه. در سناریو ی سطح صفر؛ بیمارستان یک service endpoint ای را در تعدادی URL ارائه می ده. من سپس به endpoint مذکور داکیومنتی که شامل جزیات در خواستم هست رو می فرستم.

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot start = "1400" end = "1450">
    <doctor id = "mjones"/>
  </slot>
  <slot start = "1600" end = "1650">
    <doctor id = "mjones"/>
  </slot>
</openSlotList>

سرور سپس داکیومنتی که شامل اطلاعات درخواست شده می باشد رو در جواب بر می گرداند.

POST /appointmentService HTTP/1.1
[various other headers]

<appointmentRequest>
  <slot doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointmentRequest>

من در اینجا از xml به عنوان مثال استفاده کردم؛ اما داکیونت می تونه هر چیزی باشه؛JSON؛ YAML؛ Key-values pairs یا هر فرمت شخصی دیگر.

گام بعدی من رزرو نوبت است؛ که مجددا با post کردن یک داکیومنت به endpoint اینکار رو انجام میدم.

HTTP/1.1 200 OK
[various headers]

<appointment>
  <slot doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>

اگه همه چی اوکی باشه؛ جوابی که می گه نوبت رزرو شده است رو دریافت میکنم.

HTTP/1.1 200 OK[various headers] <appointment>  <slot doctor = “mjones” start = “1400” end = “1450”/>  <patient id = “jsmith”/></appointment>

اگر مشکلی پیش بیاد

HTTP/1.1 200 OK
[various headers]

<appointmentRequestFailure>
  <slot doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
  <reason>Slot not available</reason>
</appointmentRequestFailure>

تا اینجا این یک سیستم RPC سرراست است. این بسادگی ارسال و دریافت plain old XML(POX) می باشد. اگر شما از SOAP یا XML-RPC نیز استفاده کنید, اساسا دارای رویکردی مشابه هستند, تنها تفاوت در این است که پیام های xml را در یکسری پوشش می پیچاند.

——————————————————————————————————————

سطح یک – resource ها

اولین گام در مسیر شهرت REST در RMM معرفی منابع می باشد. بنابراین حالا عوض ارسال تمام درخواست ها به یک service endpoint تنها, شروع به صحبت با resource های منحصر بفرد می باشد.

Figure 3: Level 1 adds resources

بنابراین با query اولیه من, ممکن است که دارای یک resource برای دکتر خاص باشیم.

POST /doctors/mjones HTTP/1.1
[various other headers]

<openSlotRequest date = "2010-01-04"/>

پاسخ حامل اطلاعات اولیه مشابه است, اما هر نوبت اکنون یک resource است که بصورت منحصر بفردی مشخص می شود.

HTTP/1.1 200 OK
[various headers]


<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

با resource های مشخص رزرو کردن یک نوبت به معنی post کردن به نوبت خاصی است.

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

اگر همه چیز به خوبی پیش بره؛ همانند قبل پاسخ مشابهی رو دریافت می کنم

HTTP/1.1 200 OK
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>

تفاوت اکنون این است که هرکسی در صورتی که بخواهد هرکاری رو درباره ی نوبت دهی انجام بده, از جمله رزور برخی آزمایش ها, اونها ابتدا resource نوبت رو که معمولا دارای یک URL شبیه  می باشد را گرفته, و به اون resource درخواست ها خود را post می کنند.

برای کسی مثل من این شبیه مفهوم object identity می باشه. عوض صدا زدن چندین تابع در فضا و ارسال آرگومانها, ما متدی رو در یک آبجکت خاص صدا زده و آرگومانها برای آن اطلاعات رو ارسال می کنیم.

———————————————————————————————————–

سطح دوم – HTTP Verb ها

من از HTTP POST ورب ها برای تمامی تعاملات در سطح یک و دو استفاده کردم, اما برخی افراد بجای آن یا علاوه بر آن از GET استفاده می کنند. در این سطح این مورد اون قدرها تقاوت ایجاد نمی کنه؛ آنها هر دو به عنوان مکانیزم های تونلینگ که به شما اجازه می دهند که تعامل خود از طریق HTTP رو تونل بزنید. سطح دو حرکتی به این سمت است, استفاده از HTTP verb ها تا جایی که ممکن است شبیه به روشی که آنها درون خود HTTP استفاده می شوند.

Figure 4: Level 2 addes HTTP verbs
از نظر ما لیست نوبت ها, این بدین معنی است که می خواهیم از GET استفاده کنیم.

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

پاسخ شبیه به حالتی است است که درخواست بصورت POST بود.

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

در سطح دوم, استفاده از GET برای درخواست شبیه به این؛ بسیار مهم است. HTTP کلمه ی GET رو به عنوان یک عملیات امن تعریف می کند, که این بدین معنی است که این درخواست تغییرات اساسی و بزرگی رو بر روی حالت هر چیزی اعمال نمی کنه. این مورد اجازه می ده که درخواست های امن GET رو به هر تعداد و به هر ترتیبی صدا بزنیم و هر بار نتیجه ی مشابهی رو بگیریم. یک نتیجه ی مهم در اینجا, به هر مشارکت کننده ای در مسیریابی درخواست ها اجازه میده که از کش کردن, که یک عنصر اساسی در ساخت وب بصورتی که عمل می کند؛ استفاده کنند. HTTP شامل سنجش های متفاوتی جهت پشتیبانی کش کردن هستند؛ که می تواند توسط تمام مشارکت کننده های در ارتباط استفاده بشه. با دنبال کردن نقش های HTTP می توانیم مزیت های این توانمندی رو بدست بیاوریم.

جهت رزرو یک نوبت نیاز به یک HTTP verb که حالت را عوض می کند داریم, یک POST یا PUT. من از POST مشابه که قبلا انجام دادم استفاده می کنم.

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

مصامحه میان استفاده از POST و PUT بسیار بیشتر از آن چیزی است که بخواهم در اینجا به درون اون وارد شوم؛ شاید بعدا در مورد آن مقاله ای جدا گانه در روزی دیگر بنوستم. اما می خواهم این نکته رو یادآوری کنم که برخی افراد اشتباها یک تناظر میان PUT/POST و crate/update ایجاد می کنند. انتخاب میان انها تقریبا متفاوت تر از آن است.

حتی اگر من از POST مشابهی که در سطح یک بود استفاده کنم؛ یک تفاوت اساسی دیگر در پاسخ سرویس از راه دور وجود دارد. اگه همه چیز به خوبی پیش بره, سرویس پاسخی با کد پاسخ 201 بر می گرداند که مشخص می کنه که resource جدیدی در دنیا-world- وجود دارد.

HTTP/1.1 201 Created
Location: slots/1234/appointment
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>

پاسخ 201 شامل یک ویژگی مکان بهمراه یک URL است که کلاینت می تواند از آن جهت GET کردن حالت جدید resource در آینده استفاده کند. پاسخ در اینجا همچنین شامل یک نمایش از آن resource جهت جلوگیری از اینکه کلاینت نیاز به فراخوانی بیشتر باشد نیز می باشد.

یک تفاوت دیگر وجود دارد؛ در صورتی که موردی به اشتباه و خطا بر بخورد؛ از جمله اینکه کس دیگری نوبت را قبلا رزرو کرده است.

HTTP/1.1 409 Conflict
[various headers]

<openSlotList>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

بخش مهم این پاسخ استفاده از کد های پاسخ HTTP جهت نشون دادن اینکه چیزی خطا یا اشتباه رخ داده است می باشد. در این مورد یک 409 به نظر می رسه که انتخاب مناسبی برای مشخص کردن اینکه کس دیگری قبلا resource رو به روش نامناسبی بروز رسانی کرده است؛ می باشد. عوض ارسال کد 200 اما شامل یک پاسخ خطا, در سطح دو ما صریحا از برخی از انواع پاسخ های خطا شبیه این استفاده می کنیم. این به طراحی پروتوکل بر می گرده که چه کدهایی رو استفاده کنه؛ اما در صورتی که خطایی رخ بدهد بهتر است که پاسخ غیر xx2 استفاده بشه. سطح دو استفاده از HTTP verb ها و کدهای پاسخ HTTP رو معرفی می کند.

کمی تناقض تملق گفتن وجود دارد. REST صحبت درباره ی استفاده از تمام HTTP verb ها را طرفداری می کند. آنها ممکن است با گفتن اینکه در حال یادگیری از موفقیت های عملی وب هستند این رویکرد را توجیه کنند. اما world-wide web در عمل اونقدرها از PUT یا DELETE استفاده نمی کند. دلایل توجیه کننده ای برای استفاده بیشتر از PUT و DELETE وجود دارد؛ اما دلیل و گواه وجود وب از جمله این موارد نیست.

عناصر اصلی که بوسلیه ی وب موجود پشتیبانی می شوند؛ جدایی قوی میان عملیات امن(از جمله GET) و غیر امن؛ بهمراه استفاده از کدهای حالت جهت کمک به ارتباط میان انواع خطاهایی که با آن مواجه هستید می باشد.

—————————————————————————————————–

سطح سوم: Hypermedia Controls

سطح پایانی موردی که مطرح می کنه که شما احتمالا اون رو به عنوان سرواژه بد HATEOAS شنیده اید(Hypertext A The Engine Of Application State). این مورد سوال در باره ی چگونگی گرفتن از یک لیستی از نوبت ها جهت اینکه بدانیم به چه صورت برای رزرو یک نوبت اقدام کنیم را مورد توجه قرار می دهد.

Figure 5: Level 3 adds hypermedia controls

 

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

ما با GET اولیه ی مشابهی که در سطح دوم ارسال کردیم, شروع می کنیم.

 

اما پاسخ دارای عنصر جدیدی است.

HTTP/1.1 200 OK
[various headers]

<openSlotList>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
     <link rel = "/linkrels/slot/book" 
           uri = "/slots/1234"/>
  </slot>
  <slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
     <link rel = "/linkrels/slot/book" 
           uri = "/slots/5678"/>
  </slot>
</openSlotList>

اکنون هر نوبت شامل لینکی است که شامل یک URL ای است که به ما اعلام می کند که به چه صورت می توان یک نوبت رو رزرو کنیم.

نکته ی hypermedia control ها این است که به ما می گویند در گام بعدی چه حرکتی می توان انجام داد؛ و URL مربوط resource مذکور که ما به آن جهت دستکاری کردن نیاز داریم را شامل می شود. عوض اینکه مجبور باشیم که مکانی که جهت post کردن درخواست نوبت خود را بدانیم؛ hypermedia control ها در پاسخ چگونگی انجام این کار را به ما می گویند.

مجددا POST نیز کپی ای از چیزی است که در سطح دوم انجام گرفت.

POST /slots/1234 HTTP/1.1
[various other headers]

<appointmentRequest>
  <patient id = "jsmith"/>
</appointmentRequest>

و پاسخ شامل تعدادی از hypermedia control ها برای مواردی مختلفی که در ادامه می توان انجام داد می باشد.

HTTP/1.1 201 Created
Location: http://royalhope.nhs.uk/slots/1234/appointment
[various headers]

<appointment>
  <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
  <link rel = "/linkrels/appointment/cancel"
        uri = "/slots/1234/appointment"/>
  <link rel = "/linkrels/appointment/addTest"
        uri = "/slots/1234/appointment/tests"/>
  <link rel = "self"
        uri = "/slots/1234/appointment"/>
  <link rel = "/linkrels/appointment/changeTime"
        uri = "/doctors/mjones/slots?date=20100104@status=open"/>
  <link rel = "/linkrels/appointment/updateContactInfo"
        uri = "/patients/jsmith/contactInfo"/>
  <link rel = "/linkrels/help"
        uri = "/help/appointment"/>
</appointment>

یکی از مزیت های اصلی hypermedia control این است که به سرور اجازه می دهد که شمای URI خود بدون شکستن کلاینت ها تغییر دهد. مادامی که کلاینت ها به URI لینک “addTest” نگاه می کنند؛ بنابراین تیم سرور می توانند تمام URLها را بجز نقاط ورود اولیه را دستکاری کند.

یک مزیت دیگر این است که به توسعه دهنده های کلاینت اجازه می دهد که پروتوکل های بیشتری را کشف کنند. لینک ها به توسعه دهنده های کلاینت اشاره ای می دهد که ممکن است بعدا چه کاری انجام دهند. آنها اما تمام اطلاعات را در اختیار قرار نمی دهند: هر دوی “آخرین” و “لغو” کنترل ها به URI مشابهی اشاره دارند- آنها نیاز دارند که بدوند که یکی از آن عملیات GET و دیگری DELETE است. اما حداقل آن حداقل یک نقطه ی شروع درباره ی اینکه چگونه درباره ی اطلاعات بیشار تفکر کرد و چگونگی جستجو کردن برای URI های مشابه در مستندات پروتوکل می باشد.

بطور مشابه این همچنین به تیم سرور اجازه می دهد که توانایی های جدید را با قرار دادن لینک های جدید در پاسخ اطلاع رسانی کنند. اگر توسعه دهنده های کلاینت چشمان خود را بر روی لینک های شناخته شده قرار دهند این لینک ها می تواند برای شناسایی بیشتر یک تلنگر باشند.

استاندارد مستقلی جهت چگونگی نمایش hypermedia control ها وجود ندارد. چیزی که من اینجا انجام دادم استفاده از توصیه های جاری REST در تیم Practice می باشد, که از ATOM() استفاده می کند, من یک عنصر <link> با یک ویژگی uri برای URI مقصد و یک ویژگی rel برای توصیف انواع ارتباط استفاده کردم. یک ارتباط شناخته شده(شبیه self برای یک رفرنس به خود عنصر) ساده است, هر ویژگی به آن سرور یک URI واجد شرایط است. ATOM بیان می کند که تعاریف برای لینک دهنده های  Registry of Link Relations شناخته شده می باشد. همانطور که من نوشتم اینها محدود به چیزی که توسط ATOM انجام می شود محدود شده است, که عموما به عنوان پیشروها در سطح سوم restfulness  دیده می شود.

—————————————————————————————————-

معنی سطح ها

می خوام تاکید کنم که RMM؛ هر چند یک راه مناسب برای تفکر درباره ی عناصر REST می باشد؛ اما تعریفی از سطوح REST خود نیست. Roy Fielding این را بدین صورت روشن تر کرده است که سطح سوم RMM یک پیش شرط REST است. شبیه بسیاری از اصطلاحات در نرم افزار, REST تعاریف بسیاری را بخود گرفته است, اما از آنجایی Roy Fielding اطلاح را ابداع کرد, تعریف او باید وزن بیشتری نسبت به سایرین حمل کند.

چیزی که من در مورد RMM مفید می بینم این است که آن یک راه قدم به قدم مناسب جهت فهم و درک ایده های اولیه و اساسی ورای تفکر restful مهیا می کند. همانطور که آن را به عنوان ابزاری جهت کمک به ما برای یادگیری درباره ی مفاهیم و نه فقط چیزی که باید در برخی از مکانیزم های ارزیابی استفاده شود می بینم. اما فکر نمی کنم که مثال های کافی تا بحال داشته باشیم که نشان دهد که رویکرد restful راه صحیح برای یکپارچه کردن سیستم ها است, من فکر می کنم این یک رویکرد بسیار جذاب است و یکی از مواردی است که در موارد بسیار زیادی آن را توصیه می کنم.

در صحبت کردن با Robinson, او تاکید داشت که چیزی که او درباره ی این مدل جذاب دید وقتی که برای اولین بار Leonard Richardson آنرا را ارائه داد؛ ارتباط آن با تکنیک های طراحی متدوال بود.

  • سطح یک سوال درباره ی کنترل کردن پیچیدگی با استفاده از تقسیم و غلبه را مورد هدف قرار داد؛ با شکستن یک endpoint سرویس بزرگ به resource های چندگانه.
  • سطح دوم یک مجموعه استاندارد از verb های معرفی کرد بنابراین ما موقعیت های مشابهی را به روشی مشابه کنترل می کنیم؛ و اختلافات غیر ضروری را حذف می کنیم.
  • سطح سوم قابلیت کشف کردن را مهیا کرد, که روشی را برای ایجاد یک پروتوکل با خاصیت خود مستندی بیتشر را مهیا کرد.

نتیجه مدلی است که به ما کمک می کند که درباره ی نوع سرویس HTTP, که می خواهیم توقعات افرادی که در جستجوی تعامل با آن هستند را مهیا و شکل بدهیم؛ تفکر کنیم.

درباره ی masoud@admin

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

در تلگرام هم همراه شما هستم

اگر علاقمند به معماری نرم افزار و مبحث محبوب مایکروسرویس هستید؛ در کانال با ما همراه باشید. اطلاعات مفید زیادی در این کانال انتظار شما را می کشند. فقط کافیست دکمه ی پیوستن را بفشارید.

پیوستن بستن