ডোমেন থেকে সংগ্রহস্থলগুলি অ্যাক্সেস করা হচ্ছে


14

বলুন আমাদের একটি টাস্ক লগিং সিস্টেম রয়েছে, যখন কোনও টাস্ক লগ হয় তখন ব্যবহারকারী কোনও বিভাগ এবং টাস্কটিকে 'আউটস্ট্যান্ডিং' এর স্ট্যাটাসে ডিফল্ট করে। এই উদাহরণ হিসাবে ধরে নিন যে বিভাগ এবং স্থিতি সত্তা হিসাবে প্রয়োগ করতে হবে। সাধারণত আমি এটি করতাম:

আবেদন স্তর:

public class TaskService
{
    //...

    public void Add(Guid categoryId, string description)
    {
        var category = _categoryRepository.GetById(categoryId);
        var status = _statusRepository.GetById(Constants.Status.OutstandingId);
        var task = Task.Create(category, status, description);
        _taskRepository.Save(task);
    }
}

সত্তা:

public class Task
{
    //...

    public static void Create(Category category, Status status, string description)
    {
        return new Task
        {
            Category = category,
            Status = status,
            Description = descrtiption
        };
    }
}

আমি এটি এরকম করি কারণ আমি ধারাবাহিকভাবে বলেছি যে সত্তাগুলি ভাণ্ডারগুলিতে অ্যাক্সেস করা উচিত নয়, তবে আমি যদি এটি করি তবে এটি আমার কাছে আরও বেশি অর্থবোধ করবে:

সত্তা:

public class Task
{
    //...

    public static void Create(Category category, string description)
    {
        return new Task
        {
            Category = category,
            Status = _statusRepository.GetById(Constants.Status.OutstandingId),
            Description = descrtiption
        };
    }
}

স্থিতি সংগ্রহস্থলটি যাইহোক নির্ভর করে ইনজেকশন করা হয়, সুতরাং কোনও প্রকৃত নির্ভরতা নেই এবং এটি আমার কাছে আরও বেশি অনুভূত হয় যে এটি একটি ডোমেন যা এই কাজটি বকেয়া ছাড়িয়ে ডিফল্ট করে তোলে a পূর্ববর্তী সংস্করণটিকে অ্যাপ্লিকেশন লেয়ারই সিদ্ধান্ত নিয়েছে বলে মনে হচ্ছে। যেহেতু কেন ডোমেনটিতে প্রায়শই সঞ্চিত হ'ল যদি এটি সক্ষমতা না হয়?

এখানে আরও চূড়ান্ত উদাহরণ, এখানে ডোমেন জরুরী সিদ্ধান্ত নিয়েছে:

সত্তা:

public class Task
{
    //...

    public static void Create(Category category, string description)
    {
        var task = new Task
        {
            Category = category,
            Status = _statusRepository.GetById(Constants.Status.OutstandingId),
            Description = descrtiption
        };

        if(someCondition)
        {
            if(someValue > anotherValue)
            {
                task.Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.UrgentId);
            }
            else
            {
                task.Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.SemiUrgentId);
            }
        }
        else
        {
            task.Urgency = _urgencyRepository.GetById
                (Constants.Urgency.NotId);
        }

        return task;
    }
}

আপনি জরুরি ভিত্তিতে সমস্ত সম্ভাব্য সংস্করণে পাস করতে চান এমন কোনও উপায় নেই এবং আপনি কোনওভাবেই অ্যাপ্লিকেশন স্তরে এই ব্যবসায়িক যুক্তিটি গণনা করতে চান না, তবে অবশ্যই এটি সবচেয়ে উপযুক্ত উপায় হবে?

তাহলে কি এটি ডোমেন থেকে সংগ্রহস্থলগুলি অ্যাক্সেস করার বৈধ কারণ?

সম্পাদনা: অ স্থির পদ্ধতিতে এটিও হতে পারে:

public class Task
{
    //...

    public void Update(Category category, string description)
    {
        Category = category,
        Status = _statusRepository.GetById(Constants.Status.OutstandingId),
        Description = descrtiption

        if(someCondition)
        {
            if(someValue > anotherValue)
            {
                Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.UrgentId);
            }
            else
            {
                Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.SemiUrgentId);
            }
        }
        else
        {
            Urgency = _urgencyRepository.GetById
                (Constants.Urgency.NotId);
        }

        return task;
    }
}

উত্তর:


8

আপনি ইন্টারমিক্সিং করছেন

সত্তাগুলির ভান্ডারগুলি অ্যাক্সেস করা উচিত নয়

(যা একটি ভাল পরামর্শ)

এবং

ডোমেন স্তরটি সংগ্রহস্থলগুলিতে অ্যাক্সেস করা উচিত নয়

(যতক্ষণ না আপনার সংগ্রহস্থলগুলি ডোমেন স্তরের অংশ হয়, অ্যাপ্লিকেশন স্তরটি নয়) এটির জন্য খারাপ পরামর্শ হতে পারে। প্রকৃতপক্ষে, আপনার উদাহরণগুলি কোনও সত্তা কোনও সংগ্রহস্থল অ্যাক্সেস করে না এমন কোনও ঘটনা প্রদর্শন করে না, যেহেতু আপনি স্থির পদ্ধতি ব্যবহার করছেন যা কোনও সত্তার সাথে সম্পর্কিত নয়।

আপনি যদি সৃজন শ্রেণীর কোনও স্থির পদ্ধতিতে সেই সৃষ্টিকে যুক্তি রাখতে চান না, আপনি পৃথক কারখানার ক্লাস (ডোমেন স্তরের অংশ হিসাবে!) চালু করতে পারেন এবং সেখানে সৃষ্টি যুক্তি রাখতে পারেন।

সম্পাদনা করুন: আপনার টু Updateউদাহরণ: প্রদত্ত _urgencyRepositoryএবং statusRepository বর্গ সদস্য Task, ইন্টারফেস কিছু হিসাবে সংজ্ঞায়িত করা, আপনি এখন তাদের কোনো মধ্যে উদ্বুদ্ধ করতে প্রয়োজন Taskসত্তা আগে আপনি ব্যবহার করতে পারেন Update(টাস্ক কন্সট্রাকটর মধ্যে উদাহরণস্বরূপ) এখন। অথবা আপনি এগুলি স্থিতিশীল সদস্য হিসাবে সংজ্ঞায়িত করেছেন, তবে সতর্ক থাকুন যা সহজেই একাধিক থ্রেডিং সমস্যা তৈরি করতে পারে বা একই সময়ে যখন আপনার বিভিন্ন টাস্ক সত্তার জন্য একই সময়ে বিভিন্ন টাস্ক সত্তার জন্য বিভিন্ন সংগ্রহস্থল প্রয়োজন হয় তখনই সমস্যা হতে পারে problems

এই নকশাটি Taskবিচ্ছিন্নভাবে সত্তা তৈরি করা কিছুটা শক্ত করে তোলে , Taskসত্তা সত্তার জন্য ইউনিট পরীক্ষাগুলি লিখতে আরও শক্ত, টাস্ক সত্তার উপর নির্ভর করে স্বয়ংক্রিয় পরীক্ষাগুলি লেখা আরও শক্ত এবং আপনি কিছুটা বেশি মেমরির ওভারহেড তৈরি করেন, কারণ প্রতিটি টাস্ক সত্তাকে এখনই প্রয়োজন ভাণ্ডারগুলির জন্য দুটি রেফারেন্স ধরে রাখুন। অবশ্যই, এটি আপনার ক্ষেত্রে সহনীয় হতে পারে। অন্যদিকে, পৃথক ইউটিলিটি ক্লাস তৈরি করা TaskUpdaterযা সঠিক সংগ্রহস্থলের রেফারেন্সগুলিকে রাখে প্রায়শই বা কমপক্ষে কখনও কখনও এর থেকে ভাল সমাধান হতে পারে।

গুরুত্বপূর্ণ অংশটি: TaskUpdaterএখনও ডোমেন স্তরের অংশ হবে! আপনি যে আপডেট বা তৈরি কোডটি আলাদা শ্রেণিতে রেখেছেন তার অর্থ এই নয় যে আপনাকে অন্য স্তরে স্যুইচ করতে হবে।


আমি এটি স্থিতিশীল হিসাবে যতটা স্থিতিশীল পদ্ধতিতে প্রযোজ্য তা দেখানোর জন্য সম্পাদনা করেছি। আমি কখনই কারখানার পদ্ধতিটি কোনও সত্তার অংশ না হওয়ার কথা ভাবিনি।
পল টি ডেভিস

@ পলডিটিডিভিস: আমার সম্পাদনা দেখুন
ডক ব্রাউন

আপনি এখানে যা বলছেন তার সাথে আমি একমত, তবে আমি এমন একটি সংক্ষিপ্ত অংশ যুক্ত করব যা এই বিন্দুটি আঁকবে Status = _statusRepository.GetById(Constants.Status.OutstandingId)যা একটি বিজনেস রুল , আপনি এটি পড়তে পারেন "ব্যবসায়টি সমস্ত কাজের প্রাথমিক অবস্থা নির্ধারিত হবে" এবং এ কারণেই read সেই কোড লাইনটি কোনও সংগ্রহস্থলের অন্তর্ভুক্ত নয়, যার কেবল উদ্বেগ সিআরইউডি অপারেশনের মাধ্যমে ডেটা ম্যানেজমেন্ট।
জিমি হোফা 18

@ জিমিহোফা: এইচএম, এখানে কেউই রিপোজিটরি ক্লাসের মধ্যে একটির মতো ধরণের লাইন রাখার পরামর্শ দিচ্ছিল না, না ওপি বা আমাকে - তাই আপনার বক্তব্য কী?
ডক ব্রাউন

আমি ডোমিয়ান পরিষেবা হিসাবে টাস্কআপডেটারের ধারণাটি বেশ পছন্দ করি। এটি কেবলমাত্র ডিডিডি নীতিগুলি ধরে রাখার জন্য কিছুটা ফ্যাদ বলে মনে হচ্ছে, তবে এর অর্থ এই নয় যে আমি যখনই টাস্ক ব্যবহার করি তখনই আমি সংগ্রহস্থলটি ইনজেকশন এড়াতে পারি।
পল টি ডেভিস

6

আমি জানি না যে আপনার স্থিতির উদাহরণটি সত্যিকারের কোড বা এখানে কেবল প্রদর্শনের জন্য রয়েছে, তবে এটির পক্ষে আমার কাছে বেআইনি মনে হয় যে যখন আইডিটি ধ্রুবকভাবে সংজ্ঞায়িত হয় তখন আপনাকে সত্তা হিসাবে স্ট্যাটাসটি প্রয়োগ করতে হবে (একটি সমষ্টিগত রুটের উল্লেখ না করা) implement কোডে - Constants.Status.OutstandingId। এটি "গতিশীল" স্ট্যাটাসগুলির উদ্দেশ্যকে হ্রাস করে না যা আপনি ডাটাবেসে যতগুলি চান যোগ করতে পারেন?

আমি এটিকে যুক্ত করব যে, Taskএটির (অপ্রয়োজনীয় অ্যাসেমব্লাজ অজানা) কারণে এটি নির্মাণের (স্ট্যাটাসেরোপোসিটরি থেকে যদি প্রয়োজন হয় তবে যথাযথ স্ট্যাটাস পাওয়ার প্রয়োজন সহ) নিজের TaskFactoryমধ্যে থাকার চেয়ে উপযুক্ত Taskহতে পারে।

তবে:

আমি ধারাবাহিকভাবে বলেছি যে সত্তাগুলির ভান্ডারগুলি অ্যাক্সেস করা উচিত নয়

এই বিবৃতিটি সবচেয়ে ভাল, বিভ্রান্তিকর এবং সবচেয়ে খারাপ সময়ে বিপজ্জনক এবং অপ্রয়োজনীয়।

এটি ডোমেন-চালিত আর্কিটেকচারগুলিতে বেশ সাধারণভাবে গৃহীত হয় যে কোনও সত্তা কীভাবে নিজেকে সংরক্ষণ করবেন তা জানা উচিত নয় that's এটাই অধ্যবসায় অজ্ঞতা নীতি। সুতরাং নিজেকে সংগ্রহস্থলের সাথে যুক্ত করার জন্য এর সংগ্রহস্থলের কোনও কল নেই। এটি কীভাবে (এবং কখন) অন্যান্য সত্তা সঞ্চয় করতে হবে তা জানা উচিত ? আবার, সেই দায়িত্বটি অন্য একটি অবজেক্টের অন্তর্গত বলে মনে হচ্ছে - এমন কোনও বস্তু যা প্রয়োগ প্রয়োগের পরিষেবার মতো বাস্তবায়নের প্রসঙ্গে এবং বর্তমান ব্যবহারের ক্ষেত্রে সামগ্রিক অগ্রগতি সম্পর্কে অবগত।

কোনও সত্তা কি অন্য সত্তা পুনরুদ্ধার করতে একটি সংগ্রহশালা ব্যবহার করতে পারে ? 90% সময় এটির প্রয়োজন হয় না, যেহেতু এটির সত্তাগুলির প্রয়োজনীয়তা সাধারণত তার সামগ্রীর পরিসর বা অন্যান্য অবজেক্টগুলির আড়াআড়ি দ্বারা প্রাপ্ত। কিন্তু এমন সময় আছে যখন তারা হয় না। উদাহরণস্বরূপ, আপনি যদি একটি শ্রেণিবিন্যাসিক কাঠামো গ্রহণ করেন তবে সত্ত্বাকে তাদের অভ্যন্তরীণ আচরণের অংশ হিসাবে প্রায়শই তাদের সমস্ত পূর্বপুরুষ, একটি নির্দিষ্ট নাতি-নাতালিকা ইত্যাদি অ্যাক্সেস করতে হবে। এই প্রত্যন্ত আত্মীয়দের কাছে তাদের সরাসরি রেফারেন্স নেই। অপারেশনটির প্যারামিটার হিসাবে এই আত্মীয়দের কাছাকাছি পাঠানো অসুবিধা হবে। তবে কেন সেগুলি সংগ্রহের জন্য কোনও সংগ্রহস্থল ব্যবহার করবেন না - তবে তারা মোট শিকড় হয়?

আরও কয়েকটি উদাহরণ রয়েছে। জিনিসটি হ'ল, কখনও কখনও এমন আচরণ হয় যে আপনি কোনও ডোমেন পরিষেবাতে রাখতে পারবেন না কারণ এটি কোনও বিদ্যমান সত্তায় পুরোপুরি ফিট করে। এবং তবুও, এই সত্তাকে কোনও শিকড় বা শিকড়ের সংকলন হাইড্রেট করার জন্য একটি সংগ্রহস্থল অ্যাক্সেস করা দরকার যা এটিতে যেতে পারে না।

সুতরাং কোনও সত্তা থেকে কোনও সংগ্রহস্থল অ্যাক্সেস করা নিজের পক্ষে খারাপ নয় , এটি বিভিন্ন রূপ গ্রহণ করতে পারে যা বিপর্যয় থেকে শুরু করে গ্রহণযোগ্য পর্যন্ত বিভিন্ন ডিজাইনের সিদ্ধান্তের ফলস্বরূপ ।


আমি একমত নই যে কোনও সত্তার সাথে ইতিমধ্যে যার সম্পর্ক রয়েছে সেই সত্তাকে অ্যাক্সেস করার জন্য একটি সংগ্রহস্থল ব্যবহার করা উচিত - আপনাকে সেই সত্তাটি অ্যাক্সেস করার জন্য অবজেক্ট গ্রাফটি অতিক্রম করতে সক্ষম হওয়া উচিত। এইভাবে সংগ্রহস্থলটি ব্যবহার করা হ'ল পরম সংখ্যা। আমি এখানে যা আলোচনা করছি তা হ'ল এনটাইটস হ'ল সত্তাটির এখনও কোনও রেফারেন্স নেই তবে কিছু ব্যবসায়ের শর্তে এটি তৈরি করা দরকার।
পল টি ডেভিস

আচ্ছা আপনি যদি আমাকে ভাল করে পড়ে থাকেন তবে আমরা তাতে সম্পূর্ণ একমত ...
guillaume31

2

আমি আমার ডোমেনের মধ্যে এনামস বা খাঁটি অনুসন্ধান সারণীগুলি ব্যবহার না করার এক কারণ এটি। জরুরী অবস্থা এবং স্থিতি উভয়ই রাজ্য এবং রাষ্ট্রের সাথে সরাসরি সম্পর্কিত একটি রাষ্ট্রের সাথে যুক্তি যুক্ত রয়েছে (উদাহরণস্বরূপ, আমার বর্তমান অবস্থায় আমি কী রাজ্যগুলিতে রূপান্তর করতে পারি)। এছাড়াও, একটি রাষ্ট্রকে খাঁটি মান হিসাবে রেকর্ড করে আপনি কোনও তথ্য প্রদত্ত অবস্থায় কত দিন ছিল তার মতো তথ্য হারাবেন। আমি স্ট্যাটাসগুলিকে শ্রেণীর শ্রেণিবিন্যাসের মতো প্রতিনিধিত্ব করি। (সি # তে)

public class Interval
{
  public Interval(DateTime start, DateTime? end)
  {
    Start=start;
    End=end;
  }

  //To be called by internal framework
  protected Interval()
  {
  }

  public void End(DateTime? when=null)
  {
    if(when==null)
      when=DateTime.Now;
    End=when;
  }

  public DateTime Start{get;protected set;}

  public DateTime? End{get; protected set;}
}

public class TaskStatus
{
  protected TaskStatus()
  {
  }
  public Long Id {get;protected set;}

  public string Name {get; protected set;}

  public string Description {get; protected set;}

  public Interval Duration {get; protected set;}

  public virtual TNewStatus TransitionTo<TNewStatus>()
    where TNewStatus:TaskStatus
  {
    throw new NotImplementedException();
  }
}

public class OutStandingTaskStatus:TaskStatus
{
  protected OutStandingTaskStatus()
  {
  }

  public OutStandingTaskStatus(bool initialize)
  {
    Name="Oustanding";
    Description="For tasks that need to be addressed";
    Duration=new Interval(DateTime.Now,null);
  }

  public override TNewStatus TransitionTo<TNewStatus>()
  {
    if(typeof(TNewStatus)==typeof(CompletedTaskStatus))
    {
      var transitionDate=DateTime.Now();
      Duration.End(transitionDate);
      return new CompletedTaskStatus(true);
    }
    return base.TransitionTo<TNewStatus>();
  }
}

কমপ্লিটড টাস্ক স্ট্যাটাসের বাস্তবায়নটি একই রকম হবে।

এখানে কয়েকটি বিষয় লক্ষণীয়:

  1. আমি ডিফল্ট কনস্ট্রাক্টরগুলিকে সুরক্ষিত করি। এটি তাই যখন অবিচলতা থেকে কোনও বস্তুর টান দেওয়ার সময় কাঠামোটি এটি কল করতে পারে (উভয় সত্তা ফ্রেমওয়ার্ক কোড-ফার্স্ট এবং এনএইচবারনেট তাদের যাদু করার জন্য আপনার ডোমেন অবজেক্ট থেকে প্রাপ্ত প্রক্সি ব্যবহার করে)।

  2. সম্পত্তি নির্ধারকদের অনেকেই একই কারণে সুরক্ষিত। যদি আমি কোনও ইন্টারভালের শেষ তারিখটি পরিবর্তন করতে চাই তবে আমাকে ইন্টারভাল.এন্ড () ফাংশনটি কল করতে হবে (এটি ডোমেন চালিত ডিজাইনের অংশ, অ্যানিমিক ডোমেন অবজেক্টের চেয়ে অর্থবহ ক্রিয়াকলাপ সরবরাহ করে।

  3. আমি এখানে এটি দেখায় না তবে টাস্ক একইভাবে এটি কীভাবে এটির বর্তমান অবস্থা সংরক্ষণ করে তার বিশদটি গোপন করে। আমার কাছে সাধারণত হিস্টোরিকাল স্টেটসের একটি সুরক্ষিত তালিকা থাকে যা আমি জনগণকে আগ্রহী কিনা সে সম্পর্কে জিজ্ঞাসা করার অনুমতি দিই। অন্যথায় আমি বর্তমান অবস্থাটিকে terতিহাসিক স্টেটস.সিংল (state.D अवधि.End == নাল) কে জিজ্ঞাসাবাদকারী হিসাবে বর্তমান অবস্থা প্রকাশ করি।

  4. ট্রানজিশনটো ফাংশনটি তাৎপর্যপূর্ণ কারণ এতে কোনও যুক্তি থাকতে পারে যেগুলির মধ্যে রূপান্তরটির জন্য বৈধ। আপনার যদি সবেমাত্র একটি এনাম থাকে তবে সেই যুক্তিটি অন্য কোথাও থাকতে হবে।

আশা করি, এটি আপনাকে ডিডিডি পদ্ধতির বিষয়টি আরও ভালভাবে বুঝতে সহায়তা করে।


1
এটি অবশ্যই সঠিক পদ্ধতি হবে যদি বিভিন্ন রাজ্যের আপনার রাজ্য নিদর্শন উদাহরণের মতো আলাদা আচরণ থাকে এবং এটি অবশ্যই আলোচিত বিষয়টি সমাধান করে। তবে, প্রতিটি রাজ্যের জন্য একটি শ্রেণিকে ন্যায়সঙ্গত করা আমার পক্ষে কঠিন হবে যদি তাদের আচরণের পরিবর্তে কেবল ভিন্ন মূল্যবোধ থাকে।
পল টি ডেভিস

1

আমি কিছু সময়ের জন্য একই সমস্যাটি সমাধান করার চেষ্টা করছি, আমি সিদ্ধান্ত নিয়েছি যে আমি এই জাতীয়ভাবে Task.UpdateTask () কল করতে সক্ষম হতে চাই, যদিও আমি বরং এটি ডোমেন নির্দিষ্ট করে দেব, আপনার ক্ষেত্রে সম্ভবত আমি এটিকে Task.ChangeCategory বলব (...) কোনও ক্রিয়াকলাপ নির্দেশ করতে এবং কেবল সিআরইউডি নয়।

যাইহোক, আমি আপনার সমস্যাটি চেষ্টা করেছি এবং এটি নিয়ে এসেছি ... আমার কেক আছে এবং এটি খেয়েও। ধারণাটি হ'ল ক্রিয়াকলাপগুলি সত্তার উপরে ঘটে তবে সমস্ত নির্ভরতার ইঞ্জেকশন ছাড়াই। পরিবর্তে স্থির পদ্ধতিতে কাজ করা হয় যাতে তারা সত্তার স্থিতি অ্যাক্সেস করতে পারে। কারখানাটির সমস্ত কিছু একসাথে রাখে এবং সত্তাটির কাজটি করার জন্য সাধারণত যা কিছু করা উচিত তা তা থাকবে। ক্লায়েন্ট কোড এখন পরিষ্কার এবং পরিষ্কার দেখাচ্ছে এবং আপনার সত্তা কোনও সংগ্রহস্থল ইঞ্জেকশনের উপর নির্ভর করে না।

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestProject2
{
    public class ClientCode
    {
        public void Main()
        {
            TaskFactory factory = new TaskFactory();
            Task task = factory.Create();
            task.UpdateTask(new Category(), "some value");
        }

    }
    public class Category
    {
    }

    public class Task
    {
        public Action<Category, String> UpdateTask { get; set; }

        public static void UpdateTaskAction(Task task, Category category, string description)
        {
            // do the logic here, static can access private if needed
        }
    }

    public class TaskFactory
    {      
        public Task Create()
        {
            Task task = new Task();
            task.UpdateTask = (category, description) =>
                {
                    Task.UpdateTaskAction(task, category, description);
                };

            return task;
        }

    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.