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

Richardson Maturity Model

REpresentational State Transfer یا REST مدلی بود از یک معماری مبتنی بر شبکه و برای سیستم های توزیع شده که توسط Roy Fielding در سال 2000 و در تز دکترای ایشون با عنوان Design of Network-based Software Architectures معرفی شد و بیشتر پاسخی بود بر مدل های RPC و همچنین SOAP. در مبحثی جداگانه به خود REST و ویژگی های معماری که این مدل پیشنهاد می دهد را خواهم پرداخت.

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

Richardson Maturity Model

گام هایی به سمت محبوبیتREST

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

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


 Figure 1: Steps toward REST

جهت کمک به توضیح ویژگی های خاص یک سیستم به سبک وب؛ نویسنده ها از یک مدل بلوغ سیستم های restful که توسط Leonard Richardson توسعه داده شده و در QCon در مورد آن صحبت شد؛ استفاده کردند. این مدل یک روش مفید برای تفکر در مورد استفاده از این تکنولوژی ها است؛ بنابراین به ذهنم رسید که من هم توضیحات خودم را در مورد آن ارائه بدم. (پروتوکل های آورده شده در اینجاپروتوکل در اینجا فقط برای مثال هستند؛ من فکر نمی کنم که این مثال ها ارزش کد نویسی و تست داشته باشند؛ و به همین دلیل ممکن است در جزئیات برخی مشکلات وجود داشته باشد.)

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

سطح صفر

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

Figure 2: An example interaction at Level 0

فرض کنید می خوام نوبتی را از دکترم بگیرم. نرم افزار نوبت دهی من در ابتدا نیاز داره که بدونه دکتر من برای تاریخ خاص چه نوبت هایی رو باز داره؛ به همیندلیل نرم افزار درخواستی رو برای سیستم نوبت دهی بیمارستان جهت گرفتن اون اطلاعات ایجاد می کنه. در سناریو ی سطح صفر؛ بیمارستان یک service endpoint رو با تعدادی آدرس URL ارائه می ده. بعد از اون من به endpoint مذکور داکیومنتی که شامل جزیات درخواستم هست رو می فرستم.

POST /appointmentService HTTP/1.1
[various other headers]

<openSlotRequest date = "2010-01-04" doctor = "mjones"/>
سرور سپس داکیومنتی که شامل اطلاعات درخواست شده می باشد رو در جواب بر می گردونه.
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>

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

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


POST /appointmentService HTTP/1.1
[various other headers]

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

اگه همه چی خوب پیش بره؛ جوابی که می گه نوبت رزرو شده است رو دریافت میکنم.
HTTP/1.1 200 OK
[various headers]

<appointment>
  <slot doctor = "mjones" start = "1400" end = "1450"/>
  <patient id = "jsmith"/>
</appointment>
اگر مشکلی پیش بیاد، فرض کنیدشخص دیگری قبل من نوبت رو گرفته باشه توی بخش body پاسخ یک سری پیغام خطا نیز دریافت می کنم
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 را 
در یکسری پوشش(wrapper) می پیچاند.

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

سطح یک – resource ها

اولین گام در مسیر محبوبیت REST در RMM معرفی resource ها است. در نتیجه در این سطح بچای ارسال تمام درخواست ها به یک endpoint مشخص، شروع به صحبت با resource های منحصر بفرد می کنیم.

Figure 3: Level 1 adds resources

در نتیجه با کوئری اولیه مون, ممکن است که یک 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 دقیقا شبیه به روشی که آنها توسط خود 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 شامل یک ویژگی location بهمراه یک 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 البته بهمراه یک پاسخ خطا, در سطح دو ما صریحا از برخی از انواع پاسخ های خطا شبیه این استفاده می کنیم. این به طراحی پروتوکل بر می گرده که چه کدهایی رو استفاده کنه؛ اما در صورتی که خطایی رخ بدهد بهتر است که پاسخ غیر 2xx استفاده بشه. سطح دو استفاده از ورب ها و کدهای پاسخ 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ها را بجز نقاط entry point؛ دستکاری کند.

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

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

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

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

معنی سطح ها

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

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

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

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

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

درباره ی masoud@admin

پاسخ دهید

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

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

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

پیوستن بستن