{"id":1167,"date":"2014-12-28T11:52:28","date_gmt":"2014-12-28T03:52:28","guid":{"rendered":"http:\/\/blog.turn.tw\/?p=1167"},"modified":"2014-12-28T12:00:46","modified_gmt":"2014-12-28T04:00:46","slug":"%e8%bb%9f%e9%ab%94%e6%9e%b6%e6%a7%8b%ef%bc%9adomain-driven-design","status":"publish","type":"post","link":"https:\/\/blog.turn.tw\/?p=1167","title":{"rendered":"\u8edf\u9ad4\u67b6\u69cb\uff1aDomain Driven Design"},"content":{"rendered":"<p>\u5f9e\u4e8b\u8edf\u9ad4\u958b\u767c\uff0c\u7279\u5225\u662f\u5f9eWeb\u9818\u57df\u958b\u59cb\u7684developers\uff0c\u4e00\u5b9a\u56e0\u70ba\u5404\u6846\u67b6\u666e\u904d\u63d0\u4f9bMVC\u67b6\u69cb\u7684\u95dc\u4fc2\u800c\u5927\u81f4\u4e86\u89e3MVC\u7684\u529f\u80fd\u5340\u5206\u3002<\/p>\n<p>\u7136\u800c\uff0c\u7576\u5c08\u6848\u5927\u4e86\u8d77\u4f86\uff0c\u5f88\u5feb\u5c31\u6703\u767c\u73fe\u5982\u4f55\u8b93MVC\u5206\u5de5\uff0c\u6bd4\u60f3\u50cf\u4e2d\u56f0\u96e3\uff1a<\/p>\n<p>* \u67d0\u4e9bcode\u5230\u5e95\u8a72\u653e\u5728C\u9084\u662fM\uff1f<\/p>\n<p>* \u4fee\u6539V\u5167\u7684\u67d0\u4e9bcode\u6703\u76f4\u63a5\u5f71\u97ff\u5230business logic\u7684\u904b\u4f5c\u8207\u5b89\u5168&#8230;\u9019\u4e0d\u61c9\u8a72\u5427\uff1f<\/p>\n<p>\u5f88\u986f\u7136\u5730\uff0c\u5c0d\u8edf\u9ad4\u67b6\u69cb\u7684\u77e5\u8b58\u53ea\u64c1\u6709MVC\u662f\u4e0d\u5920\u7684\u3002<\/p>\n<p>\u6211\u8209\u4e00\u500b\u66f4\u6b98\u9177\u7684\u4f8b\u5b50\u3002\u6211\u5728Reddit\u66fe\u7d93\u548c\u570b\u5916\u7684\u5de5\u7a0b\u5e2b\u8a0e\u8ad6MVC\u7684\u554f\u984c\uff1a<\/p>\n<p><a href=\"http:\/\/www.reddit.com\/r\/PHP\/comments\/28luto\/what_exactly_is_model_in_mvc\/\" title=\"What exactly is 'model' in MVC?\">What exactly is &#8216;model&#8217; in MVC?<\/a><\/p>\n<p>\u6700\u9ad8\u5206\u7684\u56de\u7b54\u5982\u4e0b\uff0c\u8b1d\u8b1d<a href=\"http:\/\/www.reddit.com\/user\/Nephand\">Nephand<\/a>\uff1a<\/p>\n<blockquote><p>\nI would say think of the &#8216;model&#8217; more as your application &#8211; it&#8217;s not a single thing. The &#8216;view&#8217; and &#8216;controller&#8217; are just a channel of communication into your application&#8217;s behaviours.<br \/>\nAnother word to replace &#8216;model&#8217; might be &#8216;domain&#8217; &#8211; see Domain Driven Design. Your application domain might need to deal with emails, or SMS but such things aren&#8217;t specific to your domain &#8211; generally speaking &#8211; so they&#8217;re services that your model is aware of (even if it&#8217;s just dispatching an event that your services listen for). They&#8217;re channels of communication into and out of your domain.<br \/>\nYour web interface talks to the application domain through a view and controller. Yet imagine you want to offer an API &#8211; this has no need of the View [simplistically put]. Also, your application might talk to the outside world through an SMS Service, or an Email Service and so on.<br \/>\nPersonally, I like Hexagonal architecture as a way of conceptualising this.<br \/>\nYour &#8216;model&#8217;\/&#8217;domain&#8217;\/&#8217;core application&#8217; then might have, for example, a persistence\/database layer which creates a &#8216;seam&#8217; between your application&#8217;s logic and the actual data storage format. This can be great, it translates out complex objects into a simple data format (SQL Rows&#8230;Mongo Documents&#8230;Redis)<br \/>\nYou mention the idea of &#8216;one model&#8217; and &#8216;one table&#8217; &#8211; and you&#8217;re right to say this part is confusing. The &#8216;model == table&#8217; approach has plenty of benefits, in that it&#8217;s pretty simple and easy to follow &#8211; but it can be limiting.<br \/>\nHaving a &#8216;seam&#8217; between your model and the data store becomes useful as it grows more complex. Within your &#8216;model&#8217; you might have one &#8216;god object&#8217; that serves as a root for multiple sub-objects. Each part can encapsulate behaviours that its containing object doesn&#8217;t need to know about &#8211; but all of that complexity still constitutes just one table row.<br \/>\nDomain Driven Design (as a primary voice but not the only one) differentiates Entity and Value Object [the classic Value Object being Money ].<br \/>\nThe Entity might represent what you&#8217;re thinking of as a &#8216;row&#8217; in your table (in the most general sense) &#8211; it has something that makes you want to be able to track its lifespan and differentiate it from others inside your application (e.g. a unique primary key &#8216;ID&#8217;).<br \/>\nQuick and dirty example: A User. A User would\/could be an Entity, with a unique identifier. A User might have an email address. The concept of an &#8216;Email Address&#8217; is not unique to a User, and so it might be a good candidate for being a Value Object that can be used by anything in your application domain\/model. So when you later need to give another Entity (say a Company) an email address, you can re-use that same value object and all of the behaviours you might want for all EmailAddresses in your domain.<br \/>\nLets say your business requires a hatred of hotmail users, and will never consider @hotmail.com a valid email address. You can then encapsulate such a rule into your EmailAddress object&#8217;s constructor [other approaches are preferred perhaps but I did say quick and dirty].<br \/>\nWhether you use EmailAddress in the context of a User or a Company, it will always throw an Exception \/ fail elegantly.<br \/>\nThis is not [typically] intended to validate user input, but to ensure that your model\/domain is meeting your hotmail hatred requirement by not allowing an EmailAddress value object for &#8220;*@hotmail.com&#8221; to be created. Whether that is passed in from a web form, over an API, or parsed in from an SMS interface shouldn&#8217;t matter to your core logic &#8211; you&#8217;re just saying &#8216;never allow this to happen even if it makes it past input validation somehow&#8217;.<br \/>\nThis comes back to your concern about having a complex model separated into different models and in turn tables. That is where different strategies for mapping your &#8216;model&#8217; to your data store come in. The Data Mapper is arguably the most flexible &#8211; and in turn potentially complicated &#8211; to implement. Patterns of Enterprise Application Architecture in combination with the Evans book Domain Driven Design can go a long way to help you understand these ideas, even if you water them down to &#8216;keep it simple [stupid]&#8217;.<br \/>\nBasically your User table (VALUES user_id, email_address) would be updated with the $user->id and $user->emailAddress->getAddress() values to store. A Company would look similar, $company->id, $company->emailAddress->getAddress().<br \/>\nThe great part about all of this is that you can start to break up and refactor your god objects into smaller parts, that can be persisted separately or not. While your god object might still serve as a &#8216;root&#8217; object, you will hopefully have been able to split its behaviours off into smaller chunks that can be tested easily, persisted easily, and even re-used where needed by other parts of your model.<br \/>\nThis is all well and good, but don&#8217;t over-engineer things. If it&#8217;s just a string value, then just let it be a string value. Until you arrive at a behaviour requirement that makes it useful to implement an abstraction then it&#8217;s probably just bloat.<br \/>\nObligatory warning This is a massive over-simplification of some more interesting concepts than I&#8217;d be able to explain &#8211; both due to lack of experience and time. Apologies to the knowledgeable if it&#8217;s a bad explanation &#8211; and to everyone else if it&#8217;s especially rambling, I don&#8217;t have time to proof read.<br \/>\nQuick Links for getting to grips with the model: &#8211; Unbreakable Domain Models &#8211; good quick intro. &#8211; Domain Driven Design &#8211; Patterns of Enterprise Application Architecture &#8211; Command Query Request Separation\n<\/p><\/blockquote>\n<p>\u70ba\u4e86\u56de\u7b54\u6211\u95dc\u65bcMVC\u7684\u554f\u984c\uff0c\u4ed6\u5f15\u7528\u4e86\u5927\u91cf\u7684\u5c08\u6709\u540d\u8a5e&#8230;\u6211\u5b8c\u5168\u770b\u4e0d\u61c2\uff0c\u4f46\u5927\u81f4\u77e5\u9053\u662f\u95dc\u65bcDomain Driven Design\u7684\u6771\u897f\u3002\u770b\u8d77\u4f86\u9019\u4e9b\u6982\u5ff5\u5df2\u7d93\u662f\u570b\u5916\u8a0e\u8ad6\u8edf\u9ad4\u67b6\u69cb\u7684\u5fc5\u5099\u77e5\u8b58\u4e86\u3002<\/p>\n<p>\u800c\u9019\u4e9b\uff0c\u5728\u53f0\u7063\u5f88\u5c11\u807d\u4eba\u63d0\u5230\u3002<\/p>\n<hr \/>\n<p>\u95dc\u65bcDDD\u5176\u5be6\u6211\u65e9\u6709\u8033\u805e\u3002Rails\u4e4b\u7236DHH\u5728<\/p>\n<p><a href=\"https:\/\/signalvnoise.com\/posts\/3375-the-five-programming-books-that-meant-most-to-me\">The five programming books that meant most to me<br \/>\n<\/a><\/p>\n<p>\u66fe\u7d93\u63d0\u5230\u4e00\u672c\u95dc\u65bcDomain Driven Design\u7684\u66f8\uff1a\u7531Eric Evans\u64b0\u5beb\u7684Domain-Driven Design: Tackling Complexity in the Heart of Software<\/p>\n<p>\u6211\u7ffb\u904e\u4e4b\u5f8c\u5be6\u5728\u662f..\u5f88\u539a\u91cd\uff0c\u518d\u52a0\u4e0a\u53c8\u662f\u539f\u6587\uff0c\u5538\u4e0d\u4e0b\u53bb\u3002<\/p>\n<p>\u4eca\u5929\u5728\u7db2\u8def\u4e0a\u627e\u5230\u6b64\u66f8\u7531\u5176\u4ed6\u4f5c\u5bb6\u7cbe\u7c21\u904e\u4e4b\u5f8c\uff0c\u63a5\u8457\u7ffb\u8b6f\u70ba\u7c21\u9ad4\u4e2d\u6587\u7684\u7248\u672c<\/p>\n<p><a href=\"http:\/\/www.infoq.com\/cn\/minibooks\/domain-driven-design-quickly\">\u9886\u57df\u9a71\u52a8\u8bbe\u8ba1\u7cbe\u7b80\u7248<\/a><\/p>\n<p>\u6211\u9084\u6c92\u6709\u5fc3\u5f97\u53ef\u4ee5\u5206\u4eab\uff0c\u56e0\u70ba\u6211\u9084\u6c92\u6709\u6d88\u5316\u5b8c\u3002<\/p>\n<p>\u4f46\u662f\u76ee\u524d\u7ffb\u95b1\u4e86\u4e09\u5206\u4e4b\u4e00\uff0c\u78ba\u5be6\u975e\u5e38\u7cbe\u5f69\u3002<\/p>\n<p>\u958b\u5377\u6709\u76ca\u554a\u670b\u53cb\uff0c\u6709\u7a7a\u7ffb\u4e00\u7ffb\u5427\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5f9e\u4e8b\u8edf\u9ad4\u958b\u767c\uff0c\u7279\u5225\u662f\u5f9eWeb\u9818\u57df\u958b\u59cb\u7684developers\uff0c\u4e00\u5b9a\u56e0\u70ba\u5404\u6846\u67b6\u666e\u904d\u63d0\u4f9bMVC\u67b6\u69cb\u7684\u95dc\u4fc2\u800c\u5927\u81f4\u4e86\u89e3M &hellip; <a href=\"https:\/\/blog.turn.tw\/?p=1167\" class=\"more-link\">\u7e7c\u7e8c\u95b1\u8b80 <span class=\"screen-reader-text\">\u8edf\u9ad4\u67b6\u69cb\uff1aDomain Driven Design<\/span> <span class=\"meta-nav\">&rarr;<\/span> <\/a><\/p>\n","protected":false},"author":1,"featured_media":1176,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[2,4],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts\/1167"}],"collection":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1167"}],"version-history":[{"count":8,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts\/1167\/revisions"}],"predecessor-version":[{"id":1175,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts\/1167\/revisions\/1175"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/media\/1176"}],"wp:attachment":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1167"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1167"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1167"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}